Trapping Rain Water

Two Pointers, Stack, Dynamic Programming

Hard

Question

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

Trapping Rain Water

Trapping Rain Water

Example

Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

Challenge

O(n) time and O(1) memory

O(n) time and O(n) memory is also acceptable.

Tags

Two Pointers Forward-Backward Traversal Array

Related Problems

  • Medium Container With Most Water

  • Hard Trapping Rain Water II

  • Candy

Analysis

Reference: https://leetcode.com/articles/trapping-rain-water/

Brute Force

最直觉的想法就是对于每个bar,找到其左边和右边最大的bar height: leftMax, rightMax,再减去bar本身的高度heights[i],就得到该处能存水的量。嵌套两重循环,每次只储存某个bar的左右最大height,因此

Time Complexity O(n^2), Space Complexity O(1)

Dynamic Programming

对于每一个bar来说,能装水的容量取决于左右两侧bar的最大值。扫描两次,一次从左向右,记录对于每一个bar来说其左侧的bar的最大高度left[i],一次从右向左,记录每一个bar右侧bar的最大高度right[i]。第三次扫描,则对于每一个bar,计算

(1)左侧最大height和当前bar的height的差值(left[i] - heights[i])

(2)右侧最大height和当前bar的height的差值(right[i] - heights[i]),

取(1),(2)中结果小的那个作为当前bar的蓄水量。最终求和得到总蓄水量。或者直接找到bari两侧最大height中最小的那个,再找与heights[i]的大于零的差值即可。

The water each bar can trap depends on the maximum height on its left and right. Thus scan twice - from left to right, and right to left and record the max height in each direction. The third time calculate the min difference between left/right height and current bar height. Sum the trapped water to get the final result.

简而言之:找左边最大和右边最大可以通过两个dp数组来存起来.

dpLeft[i] = Math.max(dpLeft[i - 1], height[i]); dpRight[i] = Math.max(dpRight[i + 1], height[i]);

Time: O(N), Space: O(2*N)

Two Pointers

@Grandyang: 我们来看一种只需要遍历一次即可的解法,这个算法需要left和right两个指针分别指向数组的首尾位置,从两边向中间扫描,在当前两指针确定的范围内,先比较两头找出较小值,如果较小值是left指向的值,则从左向右扫描,如果较小值是right指向的值,则从右向左扫描,若遇到的值比当较小值小,则将差值存入结果,如遇到的值大,则重新确定新的窗口范围,以此类推直至left和right指针重合。

Time: O(N), Space: O(1)

Monotonic Stack

@reeestart: 和largest rectangle in histogram一样的思路. 只不过histogram是用递增栈, 这道题是使用递减栈. stack里存的也是index.

每次出栈的时候是处理出栈的栈顶元素而非当前元素. 因为每次需要出栈的时候我们可以确定:

  • height[stk.peek()] < height[stk.peek().peek()], stk.peek().peek() 就是栈顶第二大的元素.

  • height[stk.peek()] < height[i]

Solution

Brute Force

class Solution {
    public int trap(int[] height) {
        if (height == null || height.length <= 2) {
            return 0;
        }
        int ans = 0;
        for (int i = 0; i < height.length; i++) {
            int leftMax = 0; 
            int rightMax = 0;
            int j = i - 1;
            int k = i + 1;
            int waterHeight = 0;
            while (j >= 0) {
                leftMax = Math.max(height[j], leftMax);
                j--;
            }
            while (k < height.length) {
                rightMax = Math.max(height[k], rightMax);
                k++;
            }
            waterHeight = Math.min(leftMax, rightMax);
            ans += Math.max(waterHeight - height[i], 0);
        }
        return ans;
    }
}

Dynamic Programming

public class Solution {
    /**
     * @param heights: an array of integers
     * @return: a integer
     */
    public int trapRainWater(int[] heights) {
        if (heights == null || heights.length < 3) {
            return 0;
        }
        int length = heights.length;

        int[] left = new int[length];
        int[] right = new int[length];

        int trapped = 0;

        // For heights[0] or heights[length - 1], the max left/right height is 0
        left[0] = 0;
        right[length - 1] = 0;

        // Keep track of the max height on the left of height[i]
        for (int i = 1; i < length; i++) {
            left[i] = Math.max(left[i - 1], heights[i - 1]);
        }

        // Keep track of the max height on the right of height[i]
        for (int j = length - 2; j >= 0; j--) {
            right[j] = Math.max(right[j + 1], heights[j + 1]);
        }

        // Calculate the total trapped water
        for (int k = 0; k < length; k++) {
            trapped += Math.max(Math.min(left[k], right[k]) - heights[k], 0);
        }

        return trapped;
    }
}

Two Pointers

class Solution {
    public int trap(int[] height) {
        if (height == null || height.length <= 2) {
            return 0;
        }
        int leftMax = 0, rightMax = 0;
        int left = 0, right = height.length - 1;
        int trapped = 0;
        while (left < right) {
            if (height[left] < height[right]) {
                if (height[left] >= leftMax) {
                    leftMax = height[left];
                } else {
                    trapped += Math.max(leftMax - height[left], 0);
                }
                left++;
            } else {
                if (height[right] >= rightMax) {
                    rightMax = height[right];
                } else {
                    trapped += Math.max(rightMax - height[right], 0);
                }
                right--;
            }
        }
        return trapped;
    }
}

Monotonic Stack

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        Stack<Integer> stk = new Stack<>();
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (stk.isEmpty() || height[i] < height[stk.peek()]) {
                stk.push(i);
            } else {
                while (!stk.isEmpty() && height[i] >= height[stk.peek()]) {
                    int h = height[stk.pop()];
                    if (!stk.isEmpty()) {
                        int len = i - stk.peek() - 1;
                        res += (Math.min(height[i], height[stk.peek()]) - h) * len;
                    }
                }
                stk.push(i);
            }
        }
        return res;
    }
}

Reference

Last updated

Was this helpful?