!42. 接雨水(Java)(单调栈)(动态规划,左缀max,右缀max)(双指针)

1 题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
!42. 接雨水(Java)(单调栈)(动态规划,左缀max,右缀max)(双指针)_第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

2 Java

2.1 方法一(单调栈,按行求)

当两个较高元素中间夹一个或几个较低元素,此时可存水,即“凹陷部分”

class Solution {
    public int trap(int[] height) {
        int res = 0;
        // !!!栈中记录的不是数组元素值,而是元素值对应索引;这是为了之后计算凹陷部分储水
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < height.length; i++) {
            // 栈顶元素 < 待入栈元素
            while(!stack.isEmpty() && height[stack.peek()] < height[i]){
                int pitIdx = stack.pop();
                // 记录栈顶元素的值,如果弹出栈顶元素后,下一个栈顶元素依然等于该值,继续弹出,直至栈顶元素值变化
                while(!stack.isEmpty() && height[stack.peek()] == height[pitIdx]){
                    stack.pop();
                }
                if(stack.isEmpty()){
                    continue;
                }
                // 弹出的索引都是凹陷部分,计算凹陷部分的储水量,计入结果
                int stackTopIdx = stack.peek();
                res += (Math.min(height[stackTopIdx], height[i]) - height[pitIdx]) * (i - stackTopIdx - 1);
            }
            // 待入栈元素入栈
            stack.add(i);
        }
        return res;
    }
}

2.2 方法二(动态规划,左缀max,右缀max)

对于某一个元素,若知道其左侧max、右侧max,即可算出该元素位置能存多少水

class Solution {
    public int trap(int[] height) {
        // 左缀 max 数组,右缀 max 数组
        int[] maxLeft = new int[height.length];
        int[] maxRight = new int[height.length];
        // maxLeft[0] = 0,无需赋值,因为左侧无元素
        for (int i = 1; i < maxLeft.length; i++) {
            maxLeft[i] = Math.max(maxLeft[i - 1], height[i - 1]);
        }
        // maxRight[maxRight.length - 1] = 0,无需赋值,因为右侧无元素
        for (int i = maxRight.length - 2; i >= 0; i--) {
            maxRight[i] = Math.max(maxRight[i + 1], height[i + 1]);
        }

        int res = 0;
        for (int i = 0; i < height.length; i++) {
            int min = Math.min(maxLeft[i], maxRight[i]);
            if(min > height[i]) {
                res += min - height[i];
            }
        }

        return res;
    }
}

2.3 方法三(双指针)

左往右处理,右往左处理,依次进行

class Solution {
    public int trap(int[] height) {
        int l = 0, r = height.length - 1;
        int maxLeft = 0, maxRight = 0;
        int res = 0;
        while(l <= r){
            // 若左max更小,先处理左边;
            if(maxLeft <= maxRight){
                // 计入结果
                res += Math.max(0, maxLeft - height[l]);
                // 指针右移,更新相关参数
                maxLeft = Math.max(maxLeft, height[l]);
                l++;
            }
            // 若右max更小,先处理右边;
            else{
                res += Math.max(0, maxRight - height[r]);
                maxRight = Math.max(maxRight, height[r]);
                r--;
            }
        }
        return res;
    }
}

你可能感兴趣的:(数组,单调栈单调队列,双指针)