题目
给定 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
解题
将该题分解为如下问题:一列一列的计算,对每一列来说,第i列所能接的雨水高度是它左边最高的和右边最高的之中矮的那方减去它本身的高度,即min(left_max[i], right_max[i]) - height[i](注意前提:min(left_max[i], right_max[i]) 要比height[i]大)
暴力解法
思路
直接一列一列计算,枚举每一列,分别从左到右遍历一遍求出左边最大和右边最大,然后将每列能承载的雨水计算出来累加。
因为要遍历两遍,所以时间复杂度为O(n2), 空间复杂度为O(1)。
代码
class Solution {
public int trap(int[] height) {
//暴力解法
int n = height.length;
int res = 0;
//遍历每一列
for(int i = 1; i < n; i++){
int left_max=0 ,right_max = 0;
//求出该列左边最高 和 右边最高
//求左边最高,从左往右遍历到当前列(注意:比较一定要包含当前列)
for(int j = 0; j <= i; j++){
left_max = Math.max(left_max, height[j]);
}
//求右边最高,从右往左遍历到当前列
for(int j = n-1;j >= i; j--){
right_max = Math.max(right_max,height[j]);
}
//计算当前列可积雨水量并累加
res += Math.min(left_max,right_max) - height[i];
}
return res;
}
}
动态规划(对暴力解法的改进)
思路
对每一列的left_max和right_max的求解进行改进,不用每次都要重复遍历一遍整个数组,用数组将每次求解的left_max和right_max保存下来。
第一种方法(使用一维数组):
从左到右遍历一遍数组求第i列左边的最高dp_leftmax[i] = max(dp_leftmax[i-1],height[i-1]
从右到左遍历一遍数组求第i列右边的最高dp_rightmax[i] = max(dp_rightmax[i+1],height[i+1]
第二种方法(使用二维数组):
创建一个二维数组dp[i][j] (i = 0~n-1; j = 1/0)
第i列左边最高: dp[i][0]= max(dp[i-1][0], height[i-1])
第i列右边最高: dp[i][1]= max(dp[i+1]
时间复杂度:O(n)
空间复杂度:O(n)
代码
动态规划之一维数组,求左/右边最高时不包括当前列,将前提放在最后判断。
class Solution {
public int trap(int[] height) {
//动态规划 一维数组
int n = height.length;
int res = 0;
//定义一维数组存储每列的左/右边最高
int[] dp_leftmax = new int[n];
int[] dp_rightmax = new int[n];
//求每列的左边最高,从左到右遍历
for(int i = 1; i < n-1; i