(栈)LeetCode42.接雨水

题目:
给定 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;
}

你可能感兴趣的:((栈)LeetCode42.接雨水)