【Leetcode】42. Trapping Rain Water

题目地址:

https://leetcode.com/problems/trapping-rain-water/

题目大意是,给定一个长 n n n数组 A A A,表示每个位置的“柱子”高度,求下雨时这些柱子能盛放的水的体积。

最优的时间 O ( n ) O(n) O(n)和空间 O ( 1 ) O(1) O(1)的双指针算法可以参考https://blog.csdn.net/qq_46105170/article/details/108571279。下面介绍动态规划解法和单调栈解法。

法1:动态规划。容易看出来,下标为 i i i的位置的正上方能盛多少水,取决于其左右两边最高柱子的较小值减去 A [ i ] A[i] A[i]。因为下雨的时候,它左右最高柱子之间盛放的水的高度,就是两个柱子的较矮者。所以我们只需要求出每个位置的左右两边的最大值即可。设 f [ i ] f[i] f[i] A [ i ] A[i] A[i]的左边的最大值,那么 f [ i ] = max ⁡ { f [ i − 1 ] , A [ i − 1 ] } f[i]=\max\{f[i-1],A[i-1]\} f[i]=max{f[i1],A[i1]}其中 f [ 0 ] = A [ 0 ] f[0]=A[0] f[0]=A[0] A [ i ] A[i] A[i]的右边的最大值也有类似的递推公式。最后重新遍历数组,求盛放水的体积即可。代码如下:

public class Solution {
    public int trap(int[] height) {
        if (height == null || height.length < 3) {
            return 0;
        }
        
        int res = 0;
        
        int[] dpl = new int[height.length], dpr = new int[height.length];
        // 求A[i]左边最大值
        dpl[0] = height[0];
        for (int i = 1; i < height.length; i++) {
            dpl[i] = Math.max(dpl[i - 1], height[i - 1]);
        }
        
        // 求A[i]右边最大值
        dpr[height.length - 1] = height[height.length - 1];
        for (int i = height.length - 2; i >= 0; i--) {
            dpr[i] = Math.max(dpr[i + 1], height[i + 1]);
        }
        
        // 每个位置盛放的水是左右两边最大值较小者减去本位置的高度。若为负,则按0算
        for (int i = 0; i < height.length; i++) {
            res += Math.max(0, Math.min(dpl[i], dpr[i]) - height[i]);
        }
        
        return res;
    }
}

时空复杂度 O ( n ) O(n) O(n)

【Leetcode】42. Trapping Rain Water_第1张图片
法2:单调栈。与上面做法不同的是,我们这里可以考虑横着切(这里类似于勒贝格积分的做法,而上面的做法是类似黎曼积分的做法)。考虑某个柱子与其两边最近的比它高的柱子所围成的水的量(比如图中的 2 , 3 , 4 2,3,4 2,3,4组成的矩形),设某个位置 i i i处,两边离它最近且比它高的柱子分别是 A [ l ] A[l] A[l] A [ r ] A[r] A[r],那么这个矩形的长就是 r − l − 1 r-l-1 rl1,而宽就是 min ⁡ { A [ l ] , A [ r ] } − A [ i ] \min\{A[l],A[r]\}-A[i] min{A[l],A[r]}A[i]。我们可以开一个严格单调下降栈,遇到比栈顶大于等于的数的时候,就将栈顶出栈,累加一下矩形面积。代码如下:

import java.util.ArrayDeque;
import java.util.Deque;

public class Solution {
    public int trap(int[] height) {
        Deque<Integer> stack = new ArrayDeque<>();
        int res = 0;
    
        for (int i = 0; i < height.length; i++) {
            while (!stack.isEmpty() && height[stack.peek()] <= height[i]) {
                int top = stack.pop();
                if (!stack.isEmpty()) {
                    res += (Math.min(height[stack.peek()], height[i]) - height[top]) * (i - stack.peek() - 1);
                }
            }
            
            stack.push(i);
        }
        
        return res;
    }
}

时空复杂度 O ( n ) O(n) O(n)

你可能感兴趣的:(LC,贪心,动态规划与记忆化搜索,算法,栈,leetcode)