这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
前言
力扣第四十二题 接雨水 如下所示:
给定 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
一、思路
这一题其实我刚开始是这么想的:如果需要接雨水必定会有一个 峰谷,即先减再增加。所以找到所有的 峰谷 和 峰顶 就能准确计算出面积了。
实现代码如下所示(错误示范):
public int trap(int[] height) {
if (height == null || height.length<3)
return 0;
int ret = 0;
int len = height.length;
// 只有出现低谷才可以容纳雨水
boolean down = false;
int left = 0;
List<Integer> boundary = new ArrayList<>();
// 从左到右找到所有的谷底
for (int i=1; i<len; i++) {
if (height[i] >= height[i-1]) {
left = i ;
down = false;
} else {
if (!down) {
// 趋势为下降
down = true;
boundary.add(left);
// 如果有两个元素了
if (boundary.size() == 2) {
// 计算高度
ret += calc(height, boundary.get(0), boundary.get(1));
boundary.clear();
down = false;
}
}
}
}
return ret;
}
public int calc(int[] height, int a, int b) {
int ret = 0;
int min = Math.min(height[a], height[b]);
for (int i=a; i<=b; i++) {
ret += Math.max(min - height[i], 0);
}
return ret;
}
但是我发现没办法处理下面这种情况:某一个 峰谷 对应的 左边峰顶 并不是紧挨着当前这个 峰谷的
所以解这道题的关键就是要找到:每个下标 i 左边的最高点和右边的最高点
总体的步骤分为一下两步:
- 找到所有下标
i对应的左最高点和右最高点 - 遍历计算面积和:下标
i对应的面积为较低的高点减去当前的高度(类似于木桶效应,取决于短板),公式为Math.min(leftMax[i], rightMax[i]) - height[i]
找到最高点的过程可以见下面的例子
举个例子(寻找最高点)
变量说明
leftMax[]:左最高点数组
此处以寻找 左最高点 作为例子,右最高点 步骤相同,故省略
下标 i 对应 左最高点 为 leftMax[i-1] 和 height[i] 中的 较大值,即 leftMax[i] = Math.max(leftMax[i-1], height[i])
所以我们只需要 从左至右 遍历数组,就可以初始化完成 左最高点数组
tips:上面这个过程就是简单的
动态规划(通过状态转移方程,自底向上求解)
二、实现
实现代码
实现代码与思路中保持一致
public int trap(int[] height) {
if (height == null || height.length<3)
return 0;
int ret = 0;
int len = height.length;
int[] leftMax = new int[len]; // i下标及其左边最高的元素
int[] rightMax = new int[len]; // i下标及其右边最高的元素
// 初始化leftMax
leftMax[0] = height[0];
for (int i=1; i<len; i++) {
leftMax[i] = Math.max(leftMax[i-1], height[i]);
}
// 初始化rightMax
rightMax[len -1] = height[len - 1];
for (int i=len-2; i>-1; i--) {
rightMax[i] = Math.max(rightMax[i+1], height[i]);
}
// 计算面积
for (int i=0; i<len; i++) {
ret += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return ret;
}
测试代码
public static void main(String[] args) {
int[] nums = {4,2,0,3,2,5};
new Number42().trap(nums);
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥