本题可以有三种解法:
- 动态规划:对每一格注水,统计每个位置高度的左边最大值和右边最大值
- 单调栈:按照层的方式注水,使用递减单调栈寻找注水区间
- 双指针:两个指针从左边向中间移动,一次遍对每一格子注水
动态规划:使用两个数组,分别遍历并记录每一位置左边高度的最大值,右边高度的最大值。
最后每个位置内的雨水大小 min(leftmax[i],rightmax[i]) - height[i]
,求和得到输出。
单调栈:栈自底向上坐标对应的高度,逐渐减小。当height[i] < stack.top()
时,i
入栈;而当height[i] > stack.top()
时,循环弹出栈顶元素top
,计算当前栈顶元素(记为left
)到i
位置区间的注水量 (i-left-1) * (min(height[i],height[left])-height[top])
。直到栈满足单调性质,i
入栈。
双指针:指针i,j
分别从数组的两端开始向中间移动,每次移动高度较小的一个,移动时给该位置注水,雨水量为min(leftmax,rightmax) - height[i/j]
,同按照格子注水。每个格子水量的高度由较小的高度决定,因此在移动时可以直接计算水量。
附上代码:
动态规划
class Solution {
public:
int trap(vector<int>& height) {
vector<int> leftmax(height.size(), 0);
vector<int> rightmax(height.size(), 0);
int out = 0;
leftmax[0] = height[0];
rightmax[height.size()-1] = height[height.size()-1];
for(int i=1; i<height.size(); i++) leftmax[i] = max(leftmax[i-1], height[i]);
for(int j=height.size()-2; j>=0; j--) rightmax[j] = max(rightmax[j+1], height[j]);
for(int i=0; i<height.size(); i++) out += min(leftmax[i], rightmax[i]) - height[i];
return out;
}
};
单调栈
class Solution {
public:
int trap(vector<int>& height) {
stack<int> stk;
int out = 0;
for(int i=0; i<height.size(); i++){
while(!stk.empty() && height[stk.top()] < height[i]){
int top = stk.top();
stk.pop();
if(stk.empty()) break;
int left = stk.top();
int width = i - left - 1;
out += (min(height[i], height[left]) - height[top]) * width;
}
stk.push(i);
}
return out;
}
};
双指针
class Solution {
public:
int trap(vector<int>& height) {
int i = 0, j = height.size()-1, out = 0;
int leftmax = 0, rightmax = 0;
while(i < j){
leftmax = max(leftmax, height[i]);
rightmax = max(rightmax, height[j]);
if(leftmax < rightmax){
out += leftmax - height[i];
i++;
} else{
out += rightmax - height[j];
j--;
}
}
return out;
}
};