1、問題描述
給定n個非負整數,每個整數表示一個寬度爲1的柱子的高度,
將這n個柱子並排放在一起,問按這種方式排列得柱子在下雨時能接多少雨水。
上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。
示例:
輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
2、解題思路
- 解決這道問題有以下幾種方法:
- 方法1:暴力法。我們可以遍歷每個柱子,然後在的左邊找到最高的柱子,再在的右邊找到最高的柱子,這樣,對於柱子而言,它能夠盛水的容量就等於與兩者之中的最小值減去柱子的高度,即.這種方法的時間複雜度爲,空間複雜度爲
算法如下所示:
輸入:高度數組height;
輸出:盛水容量ans;
算法步驟:
初始化ans = 0;
i從左向右掃描數組:
初始化leftmax = 0, rightmax = 0;
j從當前元素(i所指向的元素)向左掃描並更新leftmax:
leftmax = max(leftmax,height[j]);
k從當前元素(i所指向的元素)向右掃描並更新rightmax:
rightmax = max(rightmax,height[k]);
將min{leftmax, rightmax} - height[i]累加到ans;
*** 方法2:動態規劃。**上述暴力法每次在求解和時,都需要向左和向右掃描一遍,其實這不是必須的,我們可以提前計算出每個柱子的和,這樣在遍歷的過程中就避免了向左和向右的掃描過程。我們使用長度爲的兩個數組和來保存每個柱子的和。由於遍歷一次,故這種方法的時間複雜度爲,使用兩個額外數組,故空間複雜度爲。
- 方法3:棧。我們在遍歷數組的過程,動態維持一個棧,如果當前元素 小於等於棧頂元素,則說明棧頂元素從左邊限定了當前元素,直接把當前元素的索引壓入到棧中;如果當前元素大於棧頂元素,則說明當前元素從右邊限定了棧頂元素,而由於棧頂元素在棧中的前一個元素剛好是棧頂元素的左邊界,此時應該彈出棧頂元素,並將累加到,其中,
,
單次遍歷,每個元素最多訪問兩次(由於出棧和入棧),故時間複雜度爲。空間複雜度爲,棧最多在階梯型或平坦型條形塊結構中佔用 的空間。
算法過程:
* 使用棧s用來存儲柱子的索引,s初始爲空;
*初始化ans = 0;
*i從左到右掃描數組:
#當棧s非空並且height[i] > height[s.top]:
*彈出棧頂元素e;
*如果此時棧不爲空:
#計算當前元素與棧頂元素的距離,準備填充:distance = i - s.top - 1;
#計算界定高度,bound = min(height[i],height[s.top]) - height[e];
#將distance * bound 累加到ans中;
#棧爲空或者height[i] <= height[s.top]時,將i壓入棧中。
- 方法4:雙指針。根據方法2我們可以發現,柱子i可存儲的積水高度將由leftmax[i]和rightmax[i]中的最小值決定。
所以我們可以認爲如果一端有更高的柱子(比如右端),則積水的高度由另一端的方向決定(從左到右)。當我們發現右端的柱子不再是最高的,則從相反的方向開始遍歷(從右向左)。我們必須在遍歷時維護leftmax和rightmax,但是我們現在可以使用兩個指針交替進行,所以只需遍歷一次即可完成。
算法過程:
#初始化ans = 0;
#初始化i=0,j=size-1;
#初始化leftmax = height[0],rightmax = height[size-1];
#當i < j時:
*如果height[i] < height[j]:
#如果height[i] >= leftmax, 更新leftmax=height[i];
#否則累加到ans,ans+=leftmax - height[i];
#i++;
*否則:
#如果height[j]>=leftmax,更新rightmax = height[j];
#否則累加到ans, ans+=rightmax - height[j];
#j--;
舉個例子:
假設height=[6,4,3,7,2,4,5]
leftmax = -1,rightmax = -1;
(1)i=0,j=6,height[i] > height[j],height[j] > rightmax,故rightmax = height[j] = 5,j = 5;
(2)i=0,j=5,height[i] > height[j],height[j] < rightmax,故ans+=rightmax - height[j] = 1,j = 4;
(3)i=0,j=4,height[i] > height[j],height[j] <rightmax,故ans+=rightmax - height[j] = 1 + 3 ,j = 3;
(4)i=0,j=3,height[i] < height[j],height[i] > leftmax,故
leftmax = height[i] = 6,i = 1;
(5)i=1,j=3,height[i]<height[j],height[i] < leftmax,故ans+=leftmax - height[i] = 2,i = 2;
(6)i=2,j=3,height[i]<height[j],height[i] < leftmax,故ans+=leftmax - height[i] = 2 + 3,i = 3;
(7)i ==j
3、代碼實現
class Solution {
public:
int trap(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int leftmax = -1;
int rightmax = -1;
int ans = 0;
while(left < right){
if(height[left] < height[right]){
if(height[left] >= leftmax){
leftmax = height[left];
}
else{
ans += leftmax - height[left];
}
left ++;
}
else{
if(height[right] >= rightmax){
rightmax = height[right];
}
else{
ans += rightmax - height[right];
}
right --;
}
}
return ans;
}
};
問題擴展:如果變成三維平面,該怎麼做?
接雨水II