接雨水

74 阅读1分钟

leetcode: 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 个单位的雨水(蓝色部分表示雨水)。 

解题思路

当前位置能存多少雨水由它左侧、右侧最高柱子决定。

单调栈

  • 单调栈 单调递减,出栈时累加两个柱子中间可以存放多少雨水(仅以这两柱子为边界)。
    • 出栈元素为凹处 存放雨水
    • 栈顶元素为左侧第一个更高柱子
    • 待入栈元素为右侧第一个更高柱子

image.png

完整代码:

class Solution {
    public int trap(int[] heights) {
        int res = 0;
        Deque<Integer> stk = new ArrayDeque<>();
        for (int r = 0; r < heights.length; r++) {
            while (!stk.isEmpty() && heights[stk.getFirst()] < heights[r]) {
                int bottom = stk.removeFirst();
                if (stk.isEmpty()) break;

                int l = stk.getFirst();
                res += (Math.min(heights[l], heights[r]) - heights[bottom]) * (r - l - 1);
            }
            stk.addFirst(r);
        }
        return res;
    }
} 

双指针

每个位置可以存储多少水,由它两侧 最高柱子中较矮的那根 决定。

双指针记录 每个位置 广义上的 左侧最高柱子和右侧最高柱子。

  • e.g. 对于l来说,lMax是它左侧实际最高柱子,rMax小于等于它右侧最高柱子。

image.png

完整代码:

class Solution {
    public int trap(int[] heights) {
        int n = heights.length;
        int lMax = heights[0], rMax = heights[n - 1];
        int l = 1, r = n - 2;
        int res = 0;
        while (l <= r) {
            if (lMax < rMax) {
                lMax = Math.max(lMax, heights[l]);
                res += lMax - heights[l++];
            }
            else {
                rMax = Math.max(rMax, heights[r]);
                res += rMax - heights[r--];
            }
        }
        return res;
    }
}