目录
一、题目描述
给定
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
二、题目分析
2-1 动态规划-时间O(n) 空间O(n)
先对这个场景进行分析,对于位置 i ,能承接的雨水,跟三个因素有关
【1】 i 左边最高的柱子
【2】i 本身的高度
【3】i 右边最高的柱子
显然,i 能承接的雨水=min(左边最高,右边最高)-i的高度
同时,F(i) 不为0,如果是0,则记为0
只需要把每个位置能承接的雨水累计起来,就是一共能承接的雨水,如下
2-2 单调栈-时间O(n^2) 空间O(n)
【1】使用单调栈,求解下一个最大值;
【2】对剩余在栈底的元素进行处理
如剩余 [8,1,3] 应该转化为 [3,1,3]
剩余[3,2,1,2,1]应该转为为[2,2,1,1,1]
【3】根据当前位置跟下一个最大值之间的雨水
因为不是最优解,所以不做过多阐述,感兴趣的朋友可以看后面的代码
三、Java代码
3-1 动态规划代码
class Solution {
public int trap(int[] height) {
if (height.length == 1) {
return 0;
}
int count = 0;
int[] nextMax = getNextMax(height);
int maxLeft = height[0];
for (int i = 1; i < height.length - 1; i++) {
int h = Math.min(maxLeft, nextMax[i]) - height[i];
count += h > 0 ? h : 0;
if (height[i] > maxLeft) {
maxLeft = height[i];
}
}
return count;
}
public int[] getNextMax(int[] height) {
int[] nextMax = new int[height.length];
int maxRight = 0;
for (int i = height.length - 1; i >= 0; i--) {
nextMax[i] = maxRight;
if (maxRight < height[i])
maxRight = height[i];
}
return nextMax;
}
}
3-2 单调栈求解代码
class Solution {
public int trap(int[] height) {
if (height.length == 1) {
return 0;
}
// 单调栈 计算下一个 更大或等于height[i]的下标
Stack<Integer> stack = new Stack();
Stack<Integer> indexStack = new Stack();
int[] nextMax = new int[height.length];
for (int i = 0; i < height.length; i++) {
if (stack.isEmpty()) {
stack.push(height[i]);
indexStack.push(i);
continue;
}
while (!stack.isEmpty() && stack.peek() <= height[i]) {
nextMax[indexStack.peek()] = i;
stack.pop();
indexStack.pop();
}
stack.push(height[i]);
indexStack.push(i);
}
// 对还在栈中的元素进行处理
Integer num = null;
Integer index = null;
while (!stack.isEmpty()) {
if (num != null) {
nextMax[indexStack.peek()] = index;
height[indexStack.peek()] = num;
}
num = stack.peek();
index = indexStack.peek();
stack.pop();
indexStack.pop();
}
int flag = 0;
int count = 0;
// 计算雨水量
for (int i = 0; i < height.length; i++) {
if (nextMax[i] != 0) { // 如果存在下一个更大的数,且
if (i < flag) {
count -= height[i];
} else {
count += (nextMax[i] - i - 1) * height[i];
flag = nextMax[i];
}
}
}
return count;
}
}