给定 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 * 10^4
0 <= height[i] <= 10^5
思路:
方法一:
首先可以使用最简单的方法,也就是从左到右找到每个最高的点,然后把这点的高度向右扩展直到遇到一个更高的点时改为最高点的数值,这样就把一套的新数据存储在一个新数组中,然后再从右到左找到最高的点向左扩展,同样遇到更高点时改为最高点的数值,再把数据存储在另一个新数组中.
然后我们把这两个数组进行比较,把两者中的较小值减去当前i所在的位置,这就是当点位置的水的容量,sum不断累加就可以了.
至于在建立新数组的时候如何进行判断是否该点比之前的最高点还要高,可以使用动态规划的思想来进行简化.leftMax[i] = max(leftMax[i - 1], height[i]);这样就是把该点之前的最高点和该点进行比较,存储下两者的最高值.
代码区(动态规划):
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
vector<int> leftMax(n);
vector<int> rightMax(n);
leftMax[0] = height[0];//首先把开头的数设置为最大值
rightMax[n - 1] = height[n - 1];//因为时从后往前所以把最后一个数先设置成最大值
for(int i = 1; i < n; i++){
leftMax[i] = max(leftMax[i - 1], height[i]);//将当前的数与前面一串数的最大值进行比较
}
for(int i = n - 2; i >= 0; i--){
rightMax[i] = max(rightMax[i + 1], height[i]);
}
int sum = 0;
for(int i = 0; i <= n - 1; i++){
sum += min(leftMax[i], rightMax[i]) - height[i];//min函数求出来的是两堵墙中较低的墙,也就是木桶效应.再减去当前的高度,就是水的容量
}
return sum;
}
};
方法二:
上面这种使用动态规划的方法的空间复杂度太高了,使用了两个数组,但是其实数组中的数据就只是被用到了一次,所以没必要使用数组,我们可以使用双指针来进行简化.使用两个指针来代替两个数组.
代码区(双指针):
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int left = 1, right = n - 2;//因为下面有出现left-1.以及right+1所以需要使左指针从第二个,右指针从倒数第二个开始
int maxLeft = 0, maxRight = 0;
int temp = 0;int sum = 0;
while(left <= right){//当左指针-1小于等于右指针+1时
if(height[left - 1] < height[right + 1]){//如果左指针-1小于右指针+1说明左边是木桶的较短板,所以先处理左边
maxLeft = max(height[left - 1], maxLeft);//维护左边最大值
if(maxLeft > height[left])//如果左指针小于左边最大值的时候说明这里可以储存雨水
sum += maxLeft - height[left];//最大值减去当前的值就等于此处可以储存的雨水量
left++;//左指针向右移动
}else{//反之说明右边的短些,先处理右边
maxRight = max(height[right + 1], maxRight);//思路同上
if(maxRight > height[right])
sum += maxRight - height[right];
right--;
}
}
return sum;
}
};
方法三:
这道题同样可以使用单调栈来做,有点类似括号匹配,把两堵墙比喻成括号的两端.首先往栈中压入第一个数据的下标,然后判断下一个是否大于当前栈顶对应的值,如果大于就把栈顶弹出,然后计算这两个下标对应的"墙"之间可以储存的雨水,直到栈为空为止.(具体计算雨水方法是算出雨水的长宽然后进行乘得出)
代码区(单调栈):
class Solution {
public:
int trap(vector<int>& height) {
stack <int> stk1;//初始化一个栈
int n = height.size();
int sum = 0;
for(int i = 0; i < n; i++){
while(!stk1.empty() && height[i] > height[stk1.top()]){//当栈不为空并且i对应的数要大于栈顶对应的数
int top = stk1.top();//先把栈顶的数据存下来然后弹出栈顶
stk1.pop();
if(stk1.empty())break;//如果发现栈为空就推出循环
int left = stk1.top();//此时新的栈顶为当前次计算雨水容量的左指针
int width = i - left - 1;//雨水的宽度为i减去left再减去1,这个一目了然了吧
int heigh = min(height[i], height[left]) - height[top];//高度是先求出i也就是右指针和左指针之间的最小值(因为木桶效应所以要使用最小的数进行计算),减去之前弹出的数也就是当时的墙的高度,就是雨水的高度了
sum += heigh * width;
}
stk1.push(i);//将下一个i压入栈中
}
return sum;
}
};
sum += heigh * width;
}
stk1.push(i);//将下一个i压入栈中
}
return sum;
}
};
新手上路,有错请指正;