接雨水是一个非常经典的题目了,我在二刷的时候,终于能独立做了,在记录一下灵神的横着计算的单调栈思想.
法一: 竖着计算 奇思妙想
让我们想想,接到的雨水到底是存储哪里了呢,其实他就是凹陷部分,而什么是凹陷呢,就是从左边看,从右边看都发现不了的地方. ---------------无名氏
我们用两个数组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;}
};