题目:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 图片来自力扣官网
解法一 暴力法
思路:
1、初始化ans = 0;
2、遍历数组;
(1)遍历该值左边,找到左边数组最大值
(2)遍历该值右边,找到右边数组最大值
(3)取左右最大值的较小值,减去当前高度,累加到ans
var trap = function(height) {
let ans = 0;
for(let i = 1, len = height.length; i < len - 1; i ++){
let left_max = 0, right_max = 0;
for (let left = 0; left <= i; left ++){
left_max = Math.max(left_max, height[left]);
}
for (let right = len - 1; right >= i; right --){
right_max = Math.max(right_max, height[right]);
}
ans += Math.min(left_max, right_max) - height[i]
}
return ans;
};
解法二 动态规划
思路:
1、基于以上遍历寻找每个元素的左右最大值,可优化为记录从开端到当前位置为止的最大高度,从左右两个顺序开始;
2、遍历数组,取当前位置上左右两个最大值中较小的那个,减去当前高度,累加到ans;
var trap = function(height) {
let ans = 0,
len = height.length;
left_max = [],
right_max = [];
left_max[0] = height[0];
right_max[len - 1] = height[len - 1];
for (let i = 1; i < len; i ++){
left_max[i] = Math.max(left_max[i - 1], height[i]);
}
for (let i = len - 2; i >= 0; i --){
right_max[i] = Math.max(right_max[i + 1], height[i]);
}
for (let i = 1; i < len - 1; i ++){
ans += Math.min(left_max[i], right_max[i]) - height[i];
}
return ans;
};
解法三 双指针
思路:
在解法二的基础上,优化最大高度左右两侧较大的部分,将循环简化为一次;
1、从左到右遍历时,如果当前值比左侧最大值大,则当前位置没有雨水,更新最大值;如果比左侧最大值小,则该位置会贮存雨水,雨水高度为左侧最大值减去当前高度;
2、从右到左反之
过程参考下图,图片来源于力扣官网。
var trap = function(height) {
let ans = 0,
len = height.length;
left = 0,
right = len - 1,
left_max = right_max = 0;
while(left < right){
if(height[left] < height[right]){
height[left] > left_max ? left_max = height[left] : ans += left_max - height[left];
left ++;
}else{
height[right] > right_max ? right_max = height[right] : ans += right_max - height[right];
right --;
}
}
return ans;
};
解法四 栈
思路:
之前的思路都是累加每一列的雨水深度,该方法在每个水洼处逐行累加;
1、初始化一个栈
2、遍历
(1)如果当前高度比栈顶元素小,将当前索引压栈
(2)如果当前高度比栈顶元素大,弹出栈顶元素;记录当前高度与弹出后栈顶索引对应高度的最大值,减去当前被弹出的索引值,作为高度;计算当前索引与弹出后栈顶索引中间的距离,作为长度;计算长*高,累加到ans;重复(2)直到栈顶索引对应高度大于等于当前高度
var trap = function(height){
let ans = 0, current = 0;
let stack = [];
while (current < height.length) {
while (!!stack.length && height[current] > height[stack[stack.length - 1]]) {
let top = stack.pop();
if (!stack.length)
break;
let distance = current - stack[stack.length - 1] - 1;
let bounded_height = Math.min(height[current], height[stack[stack.length - 1]]) - height[top];
ans += distance * bounded_height;
}
stack.push(current++);
}
return ans;
}