演算法 盛最多水的容器。

2021-10-08 22:38:58 字數 2962 閱讀 9648

給你 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。

輸入:[1,8,6,2,5,4,8,3,7]

輸出:49

本題是一道經典的面試題,最優的做法是使用「雙指標」。如果讀者第一次看到這題,不一定能想出雙指標的做法。

我們先從題目中的示例開始,一步一步地解釋雙指標演算法的過程。稍後再給出演算法正確性的證明。

題目中的示例為:

[1, 8, 6, 2, 5, 4, 8, 3, 7]

^                       ^

在初始時,左右指標分別指向陣列的左右兩端,它們可以容納的水量為 min(1,7)∗8=8。

此時我們需要移動乙個指標。移動哪乙個呢?直覺告訴我們,應該移動對應數字較小的那個指標(即此時的左指標)。這是因為,由於容納的水量是由

兩個指標指向的數字中較小值 * 指標之間的距離

決定的。如果我們移動數字較大的那個指標,那麼前者「兩個指標指向的數字中較小值」不會增加,後者「指標之間的距離」會減小,那麼這個乘積會減小。因此,我們移動數字較大的那個指標是不合理的。因此,我們移動數字較小的那個指標

有讀者可能會產生疑問:我們可不可以同時移動兩個指標? 先別急,我們先假設 總是移動數字較小的那個指標 的思路是正確的,在走完流程之後,我們再去進行證明。

所以,我們將左指標向右移動:

[1, 8, 6, 2, 5, 4, 8, 3, 7]

^                    ^

此時可以容納的水量為 min(8,7)∗7=49。由於右指標對應的數字較小,我們移動右指標:

[1, 8, 6, 2, 5, 4, 8, 3, 7]

^                 ^

此時可以容納的水量為 min(8,3)∗6=18。由於右指標對應的數字較小,我們移動右指標:

[1, 8, 6, 2, 5, 4, 8, 3, 7]

^              ^

此時可以容納的水量為 min(8,8)∗5=40。兩指標對應的數字相同,我們可以任意移動乙個,例如左指標:

[1, 8, 6, 2, 5, 4, 8, 3, 7]

^           ^

此時可以容納的水量為 min(6,8)∗4=24。由於左指標對應的數字較小,我們移動左指標,並且可以發現,在這之後左指標對應的數字總是較小,因此我們會一直移動左指標,直到兩個指標重合。在這期間,對應的可以容納的水量為:min(2,8)∗3=6,min(5,8)∗2=10,min(4,8)∗1=4。

在我們移動指標的過程中,計算到的最多可以容納的數量為 49,即為最終的答案。

為什麼雙指標的做法是正確的?

雙指標代表了什麼?

雙指標代表的是 可以作為容器邊界的所有位置的範圍。在一開始,雙指標指向陣列的左右邊界,表示 陣列中所有的位置都可以作為容器的邊界,因為我們還沒有進行過任何嘗試。在這之後,我們每次將 對應的數字較小的那個指標 往 另乙個指標 的方向移動乙個位置,就表示我們認為 這個指標不可能再作為容器的邊界了。

為什麼對應的數字較小的那個指標不可能再作為容器的邊界了?

在上面的分析部分,我們對這個問題有了一點初步的想法。這裡我們定量地進行證明。

考慮第一步,假設當前左指標和右指標指向的數分別為 x 和 y,不失一般性,我們假設 x≤y。同時,兩個指標之間的距離為 t。那麼,它們組成的容器的容量為:

min(x,y)∗t=x∗t

我們可以斷定,如果我們保持左指標的位置不變,那麼無論右指標在**,這個容器的容量都不會超過 x * t了。注意這裡右指標只能向左移動,因為 我們考慮的是第一步,也就是 指標還指向陣列的左右邊界的時候。

我們任意向左移動右指標,指向的數為  y1,兩個指標之間的距離為 t 1,那麼顯然有 t1 < t,並且 min(x,y1 )≤min(x,y):

因此有:

min(x,yt )∗t1 < min(x,y)∗t

即無論我們怎麼移動右指標,得到的容器的容量都小於移動前容器的容量。也就是說,這個左指標對應的數不會作為容器的邊界了,那麼我們就可以丟棄這個位置,將左指標向右移動乙個位置,此時新的左指標於原先的右指標之間的左右位置,才可能會作為容器的邊界。

這樣以來,我們將問題的規模減小了 1,被我們丟棄的那個位置就相當於消失了。此時的左右指標,就指向了乙個新的、規模減少了的問題的陣列的左右邊界,因此,我們可以繼續像之前 考慮第一步 那樣考慮這個問題:

最後的答案是什麼?

答案就是我們每次以雙指標為左右邊界(也就是「陣列」的左右邊界)計算出的容量中的最大值。

public class solution 

else

}return ans;}}

複雜度分析

盛最多水的容器

給定 n 個非負整數 a1,a2,an,每個數代表座標中的乙個點 i,ai 畫 n 條垂直線,使得垂直線 i 的兩個端點分別為 i,ai 和 i,0 找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。注意 你不能傾斜容器,n 至少是2。乍一看很簡單,巢狀迴圈遍歷就完事了 int m...

盛最多水的容器

題目描述 給定 n 個非負整數 a1,a2,an,每個數代表座標中的乙個點 i,ai 在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 i,ai 和 i,0 找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水 說明 你不能傾斜容器,且 n 的值至少為 2。示例 輸入 1,8,6...

盛最多水的容器

給定 n 個非負整數 a1,a2,an,每個數代表座標中的乙個點 i,ai 在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 i,ai 和 i,0 找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。說明 你不能傾斜容器,且 n 的值至少為 2。圖中垂直線代表輸入陣列 1,8,...