1. 程式人生 > >(雙指標應用)盛水最多的容器

(雙指標應用)盛水最多的容器

題目描述

給定 n 個非負整數 a1,a2,…,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
在這裡插入圖片描述
說明:你不能傾斜容器,且 n 的值至少為 2。
圖中垂直線代表輸入陣列 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍色部分)的最大值為 49。

題目分析

題目從理解上還是比較簡單的,即是在陣列a中取一組i,j,使得min(a[i],a[j])×(j-i)的值最大。從這個式子來看,影響最終結果的主要有兩個因素:a[i]和a[j]中的較小值和i和j的距離。要求解這道題,最簡單的就是暴力求解了,即是對陣列中每兩個元素均進行計算,找出其中的最大值即可,不過這種方法的演算法時間複雜度達到了O(N²),顯然不是我們想要的,我們應當向O(N)的時間複雜度考慮。那麼該怎麼考慮呢?
還是從min(a[i],a[j])×(j-i)這個式子來入手,前面說了,影響這個式子就兩個因素,為了使結果儘量大並且便於求解,那麼一開始要麼應該使得min(a[i],a[j])儘量大,要麼使得j-i儘量大,顯然,在沒有遍歷陣列的情況下前者是無法做到的,但是使j-i最大是能夠做到的,即讓i和j分別指向頭尾。
在初始情況下,i指向陣列頭,j指向陣列尾,由i和j即可得出當前的容器容積,那麼下一步該怎麼移動i和j呢?是隻移動i呢還是隻移動j呢還是i和j同時移動呢?顯然不應該兩者同時移動,因為這樣很可能會漏掉實際的最大值。
這裡如果仔細思考的話,可以發現:假設a[i]和a[j]中a[i]是較小值,那麼a[i]×(j-i)就已經是所有以i為邊界的容器容積的最大值了,為什麼這麼說呢?因為a[i]是較小值,任取一個k,其結果必定是小於(a[k]<a[i])或者等於(a[k]>=a[i])a[i]×(k-i)的,而由於k<=j,因此此時以i為邊界的容器最大值必定是a[i]*(j-i)。如果下一步移動j的話,那麼i是不變的,這樣根據前面的分析,所得到的新的容器的容積肯定是不超過當前值的,因此下一步必定移動i,即是讓i++,j不變,再計算新的容器的容積,如果新的容積值大於前面的容積值,那麼就用變數max儲存這個值。移動後,決定新容器容積大小的即是新的a[i]與a[j]之間最小值了,然後下一步即是將a[i]和a[j]中的較小值所對應的i或者j往另一邊移動即可,以此類推…
因此,在每一步操作中,計算完當前容器容積大小後,只需比較a[i]與a[j],如果a[i]較小,那麼下一步就應該是i++,否則就應該是j–,最終求得最大值。程式如下:

class Solution {
public:
    int maxArea(vector<int>& height) {
        int max=0;
        int s=height.size();
        int i=0,j=s-1;
        while(i<j)
        {
            int temp=min(height[i],height[j])*(j-i);
            if(temp>max)max=temp;
            if(height[i]>=height[j])j--;
            else if(height[i]<height[j])i++;
        }
        return max;
    }
};