题目
给定 n 个非负整数表示每个宽度为 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
方法一、单调栈
寻找位置两边的最大高度,
维护一个单调栈,单调栈存储的是下标,满足从栈顶到栈底递增顺序。
栈顶即为坑的下标,栈顶的下一个元素为坑左边的下标,当前如果大于栈顶,就为坑右边的下标。
水坑的高为:h = Math.min(height[left], height[i]) - height[top]
水坑的宽为:w = i-left-1
PS:单调栈中至少有2个元素才能计算
class Solution {
public int trap(int[] height) {
Deque<Integer> st = new LinkedList<>(); //存储下标
int n = height.length;
int res = 0;
for(int i = 0; i < n; i++){
while(!st.isEmpty() && height[i] > height[st.peek()]){
int top = st.pop();
if(st.isEmpty()) break; //单调栈中至少有2个元素才能计算,否则计算结束
int left = st.peek();
res += (Math.min(height[left], height[i]) - height[top]) * (i-left-1);
}
st.push(i);
}
return res;
}
}
方法二、动态规划
对于下标 i,下雨后水能到达的最大高度等于下标 i两边的最大高度的最小值,下标i处能接的雨水量等于下标 i处的水能到达的最大高度减去 height[i]。
leftMax[i]
表示i左边能到达的最大高度
rightMax[i]
表示i右边能到达的最大高度
class Solution {
public int trap(int[] height) {
int n = height.length;
if (n == 0) {
return 0;
}
int[] leftMax = new int[n];
leftMax[0] = height[0];
for (int i = 1; i < n; ++i) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
int[] rightMax = new int[n];
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
int res = 0;
for (int i = 0; i < n; ++i) {
res += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return res ;
}
}