题目地址:力扣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 == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
解法一、栈
思路:
有水的地方就是左边和右边都有比它高的地方。
我们可以按行来看,这样的话可以用到栈,当前位置比栈顶高的话,就计算水洼面积。
以例1的图为例。位置0的时候入栈,位置1的时候比位置0高了,让位置0出栈,但是位置0左边是没东西的(即栈为空),所以不管它,位置1入栈,继续遍历。
到了2的时候,比栈顶(位置1)要低,所以2也入栈。
位置3的时候比栈顶(位置2)高,位置2出栈,栈没空,所以取left为栈顶位置(即位置1),取right为当前位置3. 宽度w就是3-1-1=1.而高度是当前位置高度和栈顶位置高度的最小值-之前栈顶的高度,也是1.所以第一个水洼面积是1.
继续
到了4的时候友低了,所以入栈。5也是入栈。
6的时候比栈顶高,5出栈,此时栈顶是4,那这个时候ans=ans+(6-4-1)*(6的高度和4的高度的最小值-5的高度)。
到7的时候,也比栈顶(4)高,4出栈,此时栈顶是3,此时栈顶是3.ans=ans+(7-4-1)*(7的高度和3的高度的最小值-4的高度)。
以此类推。
代码:
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()==0)return 0;
stack<int> s;
int ans=0;
int t;
for(int i=0;i<height.size();i++)
{
while(s.size()&&height[s.top()]<height[i])
{
t=s.top();
s.pop();
if(!s.size())break;
int l=s.top();
int r=i;
int w=r-l-1;
ans+=(min(height[i],height[l])-height[t])*w;
}
s.push(i);
}
return ans;
}
};
解法二、动态规划
思路:
上一种方法是按行来计算,那么这种方法是按列计算。
每一个位置的积水都是,它往左边去找到的最高的柱子和它往右边去找到的最高的柱子的最小值减去它本身(当然前提是这个最小值比它本身高)。
在寻找最值的路上,发现有很多重复的步骤,所以这里就可以用到动态规划。
直接看代码吧,比较容易懂。
代码:
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()==0)return 0;
int left[20001];
left[0]=0;
int right[20001];
right[height.size()-1]=0;
for(int i=1;i<height.size();i++)
{
left[i]=max(left[i-1],height[i-1]);
}
for(int i=height.size()-2;i>=0;i--)
{
right[i]=max(right[i+1],height[i+1]);
}
int ans=0;
for(int i=1;i<height.size();i++)
{
if(min(left[i],right[i])>height[i])ans=ans+min(left[i],right[i])-height[i];
}
return ans;
}
};