【刷穿LeetCode】42. 接雨水

目录

一、题目描述

 二、题目分析

2-1 动态规划-时间O(n) 空间O(n)

2-2 单调栈-时间O(n^2) 空间O(n)

三、Java代码

3-1 动态规划代码

3-2 单调栈求解代码


一、题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

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

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

 二、题目分析

2-1 动态规划-时间O(n) 空间O(n)

先对这个场景进行分析,对于位置 i ,能承接的雨水,跟三个因素有关

【1】 i 左边最高的柱子

 maxLeft(i)=max(height[0],height[1]...height[i-1])

【2】i 本身的高度 

height[i]

【3】i 右边最高的柱子

maxRight(i)=max(height[i+1],height[i+2]...height[len-1])

显然,i 能承接的雨水=min(左边最高,右边最高)-i的高度

F(i)=min(maxLeft(i),maxRight(i))-height[i]

同时,F(i) 不为0,如果是0,则记为0

只需要把每个位置能承接的雨水累计起来,就是一共能承接的雨水,如下

F(n)=F(n-1)+min(maxLeft(i),maxRight(i))-height[i]

2-2 单调栈-时间O(n^2) 空间O(n)

【1】使用单调栈,求解下一个最大值;

【2】对剩余在栈底的元素进行处理

如剩余 [8,1,3] 应该转化为 [3,1,3]

剩余[3,2,1,2,1]应该转为为[2,2,1,1,1]

【3】根据当前位置跟下一个最大值之间的雨水

因为不是最优解,所以不做过多阐述,感兴趣的朋友可以看后面的代码

三、Java代码

3-1 动态规划代码

class Solution {

    public int trap(int[] height) {
        if (height.length == 1) {
            return 0;
        }

        int count = 0;

        int[] nextMax = getNextMax(height);

        int maxLeft = height[0];
        for (int i = 1; i < height.length - 1; i++) {
            int h = Math.min(maxLeft, nextMax[i]) - height[i];
            count += h > 0 ? h : 0;

            if (height[i] > maxLeft) {
                maxLeft = height[i];
            }
        }

        return count;
    }

    public int[] getNextMax(int[] height) {
        int[] nextMax = new int[height.length];
        int maxRight = 0;
        for (int i = height.length - 1; i >= 0; i--) {
            nextMax[i] = maxRight;
            if (maxRight < height[i])
                maxRight = height[i];
        }
        return nextMax;
    }
}

3-2 单调栈求解代码

class Solution {

    public int trap(int[] height) {
        if (height.length == 1) {
            return 0;
        }

        // 单调栈 计算下一个 更大或等于height[i]的下标
        Stack<Integer> stack = new Stack();
        Stack<Integer> indexStack = new Stack();
        int[] nextMax = new int[height.length];
        for (int i = 0; i < height.length; i++) {
            if (stack.isEmpty()) {
                stack.push(height[i]);
                indexStack.push(i);
                continue;
            }
            while (!stack.isEmpty() && stack.peek() <= height[i]) {
                nextMax[indexStack.peek()] = i;
                stack.pop();
                indexStack.pop();
            }
            stack.push(height[i]);
            indexStack.push(i);
        }
        // 对还在栈中的元素进行处理
        Integer num = null;
        Integer index = null;

        while (!stack.isEmpty()) {
            if (num != null) {
                nextMax[indexStack.peek()] = index;
                height[indexStack.peek()] = num;
            }
            num = stack.peek();
            index = indexStack.peek();
            stack.pop();
            indexStack.pop();
        }

        int flag = 0;
        int count = 0;

        // 计算雨水量
        for (int i = 0; i < height.length; i++) {
            if (nextMax[i] != 0) { // 如果存在下一个更大的数,且
                if (i < flag) {
                    count -= height[i];
                } else {
                    count += (nextMax[i] - i - 1) * height[i];
                    flag = nextMax[i];
                }
            }
        }

        return count;
    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值