接雨水是一个非常经典的题目了,我在二刷的时候,终于能独立做了,在记录一下灵神的横着计算的单调栈思想.
法一: 竖着计算 奇思妙想
让我们想想,接到的雨水到底是存储哪里了呢,其实他就是凹陷部分,而什么是凹陷呢,就是从左边看,从右边看都发现不了的地方. ---------------无名氏
我们用两个数组left[i]和right[i]分别记录 height[0...i]的最大值 和 height[len-1...i]的最大值.
也就是
left[i] = max(left[i-1],height[i])
right[i] = max(right[i+1],height[i])
那么 每次 就是 ans += min(left[i],right[i]) - height[i]
代码如下:
class Solution {
public:
int trap(vector<int>& height) {
int len=height.size();
vector<int> left(len,0);
vector<int> right(len,0);
left[0]=height[0];
right[len-1]=height[len-1];
for(int i=1;i<len;i++){
left[i]=max(left[i-1],height[i]);
}
for(int i=len-2;i>=0;i--){
right[i]=max(right[i+1],height[i]);
}
int ans=0;
for(int i=0;i<len;i++){
ans+=min(left[i],right[i]) - height[i];
}
return ans;
}
};
法二: 横着计算 单调栈
每次遇到比当前栈顶元素小的等的就压入栈,而遇到大的就弹出栈,也就是说这个栈就是递减的
当弹出的时候就意味着,右边来了个更大的(右边界),也就满足了凹陷一半的条件,为什么说是一半呢,因为凹陷还需要左边有一个更大的,也就是当前栈在弹出一个之后还要有一个元素(左边界),那么 ans+=(l-r+1) * height,
总的来说就是, 当遇到一个更大的元素(右边界),弹出一个元素作为bottom,然后再弹出一个元素作为左边界, 没有左边界就是 0
代码如下:
class Solution {
public:
//法二: 横着算
int trap(vector<int>& height) {
stack<int> stk;
int ans=0;
for(int i=0;i<height.size();i++){
//由于我们单调栈的构建,栈顶元素对应的height是最低的 他才是bottom 而栈顶下面的那个才是左柱子
while(!stk.empty() && height[i]>=height[stk.top()]){
int bottom=height[stk.top()];
stk.pop();
if(stk.empty()) break; //就是只有一个底 左边没有柱子
int left=height[stk.top()];
ans+=(i-stk.top()-1) * (min(left,height[i])-bottom); //这里的height[i]就是right
}
stk.push(i);
}
return ans;
}
};