题目
给定 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 == height.length
1 <= n <= 2 * 10^4
-
解题方法
动态规划
每一列能够接的雨水量有该列左右两侧的最大值的较小值决定,因此可以预处理处每一列对应的左侧和右侧最大值,进而计算该列能够承接的雨水量。
时间复杂度O(n)
,空间复杂度O(n)
。
C++代码:class Solution {
public:
int trap(vector<int>& height) {
int size = height.size();
vector<int> rest(size, INT_MAX);
int lmax = height[0], rmax = height[size-1];
for(int i=0; i<size; i++) {
lmax = lmax > height[i] ? lmax : height[i];
rmax = rmax > height[size-1-i] ? rmax : height[size-1-i];
rest[i] = rest[i] < lmax ? rest[i] : lmax;
rest[size-1-i] = rest[size-1-i] < rmax ? rest[size-1-i] : rmax;
}
int result = 0;
for(int i=0; i<size; i++) {
result += (rest[i]-height[i]);
}
return result;
}
};
单调栈
使用递增单调栈保存递减序列,当寻找到下一个更大元素时,弹出当前元素,并根据前一个元素对当前元素所在位置进行雨水填充。
时间复杂度O(n)
,空间复杂度O(n)
。
C++代码:class Solution { public: int trap(vector<int>& height) { int size = height.size(); stack<int> st; int result = 0; for(int i=0; i<size; i++) { while(!st.empty() && height[i]>height[st.top()]) { int mid = height[st.top()]; st.pop(); if(!st.empty()) { result += (min(height[i], height[st.top()])-mid) * (i-st.top()-1); } } st.push(i); } return result; } };
双指针
左右指针分别从左端和右端向中间移动,过程中维护左侧最大值与右侧最大值,通过
height[left]
和height[right]
决定填充以及指针移动:- 如果
height[left]<height[right]
,则对于left
处的元素必有lmax<lmax
,故通过lmax
填充left
处,并将left
右移。 - 如果
height[left]≥height[right]
,则对于right
处的元素必有lmax≥rmax
,通过rmax
填充right
处,并将right
左移
时间复杂度O(n)
,空间复杂度O(1)
。
C++代码:
class Solution {
public:
int trap(vector<int>& height) {
int result = 0;
int left = 0;
int right = height.size()-1;
int lmax = 0;
int rmax = 0;
while(left<right) {
lmax = max(height[left], lmax);
rmax = max(height[right], rmax);
if(height[left]<height[right]) {
result += lmax-height[left];
left++;
}
else {
result += rmax-height[right];
right--;
}
}
return result;
}
};