给定 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
先从暴力开始入手,我们从当前的位置向两边拓展,然后如果遇到比当前大的就更新,要注意噢,在同一方向的上的。然后我们分别得到左右两边两个方向的最大值,我们取最小的,然后与当前值相减就得到能存储的水量,然后我们将没一部分的都加在一起,就可以得到所有能存的水量
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
int len=height.size();
for(int t=1;t<len-1;t++){
int left=t-1;
int left_max=height[t];
while(left>=0){
left_max=max(left_max,height[left--]);
}
int right=t+1;
int right_max=height[t];
while(right<len){
right_max=max(right_max,height[right++]);
}
sum+=(min(left_max,right_max)-height[t]);
}
return sum;
}
};
我们开始优化一下,我们要找的是最大值,我们是否可以用两个值记录我们当前最大值呢?我们可不可以使用两个数组,定义 数组第 i 个代表左边的最高墙(另一个数组则为右边的最高墙)
这里要注意,当我们找左边的最大的时候,我们可以顺序,因为我们左边最大一定是当前的位置的左边,也就是左边最大值一定是已经访问过的。
而右边最大,我们要逆序,为什么呢,如果我们顺序,我们无法判断当前右边的状态,因为右边的状态是未知的,那么我们就从右边开始遍历。
这里要注意起始位置,设想一下在阶梯的最左边是不存在左边最大值的,因为他已经是最左边的,所以我们就默认为零,同样的,我们在阶梯的最右边,我们是不存在右边最大值的,因为我们已经是最右边了,所以我们也默认为零。
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
int len=height.size();
vector<int>left_max(len,0);
vector<int>right_max(len,0);
for(int i=1;i<len;i++){
left_max[i]=max(left_max[i-1],height[i-1]);
}
for(int i=len-2;i>=0;i--){
right_max[i]=max(right_max[i+1],height[i+1]);
}
for(int i=1;i<len-1;i++){
int n=min(left_max[i],right_max[i]);
if(n>height[i])
sum+=(n-height[i]);
}
return sum;
}
};
我们还可以进一步将right_max数组进行优化,上面提到,我们right_max一定是我们遍历过的,所以我们完全可以不用单独一个循环去保存记录左边最大值。
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
int len=height.size();
int left_max=0;
vector<int>right_max(len,0);
for(int i=len-2;i>=0;i--){
right_max[i]=max(right_max[i+1],height[i+1]);
}
for(int i=1;i<len-1;i++){
left_max=max(left_max,height[i-1]);
int n=min(left_max,right_max[i]);
if(n>height[i])
sum+=(n-height[i]);
}
return sum;
}
};