给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
当两个较高元素中间夹一个或几个较低元素,此时可存水,即“凹陷部分”
class Solution {
public int trap(int[] height) {
int res = 0;
// !!!栈中记录的不是数组元素值,而是元素值对应索引;这是为了之后计算凹陷部分储水
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < height.length; i++) {
// 栈顶元素 < 待入栈元素
while(!stack.isEmpty() && height[stack.peek()] < height[i]){
int pitIdx = stack.pop();
// 记录栈顶元素的值,如果弹出栈顶元素后,下一个栈顶元素依然等于该值,继续弹出,直至栈顶元素值变化
while(!stack.isEmpty() && height[stack.peek()] == height[pitIdx]){
stack.pop();
}
if(stack.isEmpty()){
continue;
}
// 弹出的索引都是凹陷部分,计算凹陷部分的储水量,计入结果
int stackTopIdx = stack.peek();
res += (Math.min(height[stackTopIdx], height[i]) - height[pitIdx]) * (i - stackTopIdx - 1);
}
// 待入栈元素入栈
stack.add(i);
}
return res;
}
}
对于某一个元素,若知道其左侧max、右侧max,即可算出该元素位置能存多少水
class Solution {
public int trap(int[] height) {
// 左缀 max 数组,右缀 max 数组
int[] maxLeft = new int[height.length];
int[] maxRight = new int[height.length];
// maxLeft[0] = 0,无需赋值,因为左侧无元素
for (int i = 1; i < maxLeft.length; i++) {
maxLeft[i] = Math.max(maxLeft[i - 1], height[i - 1]);
}
// maxRight[maxRight.length - 1] = 0,无需赋值,因为右侧无元素
for (int i = maxRight.length - 2; i >= 0; i--) {
maxRight[i] = Math.max(maxRight[i + 1], height[i + 1]);
}
int res = 0;
for (int i = 0; i < height.length; i++) {
int min = Math.min(maxLeft[i], maxRight[i]);
if(min > height[i]) {
res += min - height[i];
}
}
return res;
}
}
左往右处理,右往左处理,依次进行
class Solution {
public int trap(int[] height) {
int l = 0, r = height.length - 1;
int maxLeft = 0, maxRight = 0;
int res = 0;
while(l <= r){
// 若左max更小,先处理左边;
if(maxLeft <= maxRight){
// 计入结果
res += Math.max(0, maxLeft - height[l]);
// 指针右移,更新相关参数
maxLeft = Math.max(maxLeft, height[l]);
l++;
}
// 若右max更小,先处理右边;
else{
res += Math.max(0, maxRight - height[r]);
maxRight = Math.max(maxRight, height[r]);
r--;
}
}
return res;
}
}