42.接雨水
问题:给定 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 个单位的雨水(蓝色部分表示雨水)。
输入:height = [4,2,0,3,2,5]
输出:9
思路:这道题最主要的是将问题分解,不要从全局看,而是从局部看。参考labuladong大佬题解。
这道题针对局部,也就是对于位置i
,能装多少水
通过观察可以发现,位置i
装水的多少取决于其左边部分的最大值lMax
以及其右半部分的最大值rMax
中的最小值。
于是将局部扩散到全部,可以想到一个暴力解法。对于数组中位置i
,求出其左半部分以及右半部分的最大值,然后计算出当前位置i
的装水量。
暴力解法的时间复杂度为O( n 2 n^2 n2),因为对于数组中的每一个位置,都需要遍历数组,寻找其左半部分以及右半部分的最大值。这个操作中包含许多重复操作。
可以使用两个备忘录提前计算好每个位置的左右部分的最大值。
- 备忘录优化时间复杂度
class Solution {
public int trap(int[] height) {
int len = height.length;
int[] lMax = new int[len], rMax = new int[len];
lMax[0] = height[0];
rMax[len - 1] = height[len - 1];
for(int i = 1; i < len; i++){
lMax[i] = Math.max(lMax[i - 1], height[i]);
}
for(int i = len - 2; i >= 0; i--){
rMax[i] = Math.max(rMax[i + 1], height[i]);
}
int res = 0;
for(int i = 0; i < len; i++){
res += Math.min(lMax[i], rMax[i]) - height[i];
}
return res;
}
}
- 双指针优化空间复杂度
备忘录的解法将时间复杂度降低到O(n),但是空间复杂度为O(n),使用双指针可以巧妙的进行优化。
下面代码中的**lMax
是 height[0..left]
中最高柱子的高度,rMax
是 height[right..end]
的最高柱子的高度**。虽然变量含义发生了一些变化,但是核心思想没变。
以上图为例,对于当前位置left
来说,只要保证rMax >= lMax
就行,即只要位置left的右半部分存在高度高于lMax的柱子,则位置left
的装水量只和lMax
有关。至于rMax
是否为left
右半部分的最大值,不重要。对于位置right
同理。
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int lMax = 0, rMax = 0;
int res = 0;
while(left < right){
lMax = Math.max(lMax, height[left]);
rMax = Math.max(rMax, height[right]);
if(lMax < rMax){
res += lMax - height[left];
left++;
} else {
res += rMax - height[right];
right--;
}
}
return res;
}
}
整理思路,记录博客,以便复习。若有误,望指正~