#力扣 LeetCode1046. 最後一塊石頭的重量 @FDDLC
技術標籤:LeetCode
42. 接雨水
題目描述
給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。
示例1:
輸入:height = [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:
輸入:height = [4,2,0,3,2,5]
輸出:9
提示:
-
n
=
=
h
e
i
g
h
t
.
l
e
n
g
t
h
n == height.length
n==heig
- 0 ≤ n ≤ 3 ∗ 1 0 4 0 \le n \le 3 * 10^4 0≤n≤3∗104
- 0 ≤ h e i g h t [ i ] ≤ 105 0 \le height[i] \le 105 0≤height[i]≤105
題解:
這題可以從兩個方向考慮,一種是豎著,一種是橫著。
法一(豎著):
只考慮 i
位置上方能放幾格水。那麼很容易想到 i
位置上方水的數量的表示式:max{min{ i 左側的最大值, i 右側的最大值} - height[i], 0}
。
對所有位置的結果累加求和就是最終結果。然後再求上述表示式的結果時,就有一些值得探究的地方了。
-
最基礎的解法就是暴力了,對於每個位置
i
[0...i-1]
的最大值和[i+1,n-1]
的最大值。但是這題資料肯定過不去,程式碼就不貼了。 -
其實可以不用那麼暴力,我們可以額外使用兩個陣列
leftMax
和rightMax
,leftMax[i]
表示[0...i]
的最大值,right[i]
表示[i...n-1]
的最大值。從左往右遍歷一遍,生成leftMax
;從右往左一遍,生成rightMax
。程式碼如下:class Solution { public: int trap(vector<int>& height) { int n = height.size
-
其實還可以使用雙指標,做到額外空間複雜度 O ( 1 ) O(1) O(1) 。具體就是:使用兩個指標
l
和r
,開始分別指向0
和n - 1
,兩個變數leftMax
和rightMax
,分別表示l
左邊的最大值和r
右邊的最大值,開始時為arr[0]
和arr[n - 1]
,每一步讓l
移動或者r
移動,具體分情況討論:- 若
leftMax <= rightMax
,此時可以求出l
位置上方的水量。因為rightMax
是arr[r + 1...n - 1]
的最大值,而l
的右側還有一個未知區域,所以l
右側最大值一定不會小於rightMax
。leftMax
代表l
左側的最大值,因為leftMax <= rightMax
,可知leftMax
是l
位置的瓶頸。故l
位置上方的水量為max{leftMax - arr[l], 0}
,然後讓l
向右移動,並且移動前需要更新leftMax = max{leftMax, arr[l]}
。 - 若
leftMax > rightMax
,分析同 1。
程式碼如下:
class Solution { public: int trap(vector<int>& height) { int n = height.size(); if ( n < 3 ) return 0; int lmax = height[0], rmax = height[n - 1]; int ret = 0, l = 1, r = n - 2; while ( l <= r ) { if ( lmax <= rmax ) { ret += max( lmax - height[l], 0 ); lmax = max( lmax, height[l++] ); } else { ret += max( rmax - height[r], 0 ); rmax = max( rmax, height[r--] ); } } return ret; } }; /* 時間:0ms,擊敗:100.00% 記憶體:13.8MB,擊敗:95.18% */
- 若
法二(橫著):
就是考慮一個空格往左往右最長能延長到什麼地方(按層的意思)。
我們可以觀察到,當柱子之間形成一個 u
形槽時,就會積累雨水。我們維護一個棧底到棧頂嚴格遞減的單調棧,如果 arr[i] >= arr[stk.top()]
,說明此時形成了 凹槽 ,將棧頂元素彈出,記為 top
,此時增加的雨水量是:(i - stk.top() - 1) * (min(arr[i], arr[stk.top()]) - arr[top])
。
法二程式碼:
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if ( n < 3 ) return 0;
vector<int> stk;
int top, ret = 0;
for ( int i = 0; i < height.size(); ++i ) {
while ( stk.size() && height[stk.back()] <= height[i] ) {
top = stk.back();
stk.pop_back();
if ( !stk.size() ) break;
ret += ( i - stk.back() - 1 ) * (min( height[i], height[stk.back()] ) - height[top]);
}
stk.push_back( i );
}
return ret;
}
};
/*
時間:8ms,擊敗:90.86%
記憶體:13.9MB,擊敗:94.12%
*/