42. 接雨水(java实现)--LeetCode

题目

42. 接雨水

给定 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
  • 0 <= n <= 3 * 10^4
  • 0 <= height[i] <= 10^5

解法1:暴力(左右边界)

/**
 * 思路:
 * 核心思路:
 * 找到每个坑,计算面积和
 * 每个坑都有左右边界,我们要确定好边界
 *
 * 只要右边界没走到数组的末尾就一直循环
 * 找左边界:记录左边界,只要下一个值比左边界小,就确定了左边界。否则,更新左边界为下一个值
 * 之后向后找右边界:
 * 记录最大的棒子
 * 大于等于左边界就是右边界,否则一直向前找
 * 如果找到数组末尾都没找到大于等于左边界的值,就把最大的棒子作为右边界
 * 找到左右边界后计算坑的面积:min(height[l],hei[r])*(r-l-1)-夹在左右边界中的各个柱子的高度
 */
     public int trap(int[] height) {
        int sum=0,bar_sum=0,l=0,r = 0;
        while (r<height.length-1) {
            for (int i = l+1; i < height.length; i++) {
                if (i==0||height[i] >= height[i - 1]) l = i;
                else break;
            }
            int  max_bar=l+1;
            for (int i = l + 1; i < height.length; i++) {
                //如果当前棒子大于max_bar就记录
                max_bar=height[max_bar]>height[i]?max_bar:i;
                if (height[i] >= height[l]) {
                    r = i;
                    break;
                }
            }
            if (max_bar>=height.length)break;
            //如果没有找到大于等于左棒子的,就把max_bar作为右棒子
            if (r<=l) {
                r=max_bar;
            }
            int area = Math.min(height[l], height[r]) * (r - l - 1);
            for (int j = l + 1; j < r; j++) {
                bar_sum += height[j];
            }
            sum += area - bar_sum;
            l = r;
            bar_sum = 0;
        }
        return sum;
    }

时间复杂度:On^2

空间复杂度:O1
在这里插入图片描述

解法2:暴力(纵向注水)

/**
 * 思路:
 * 核心思路:给每一列的柱子注水,每个的柱子的最大注水量
 *
 * 头和尾的柱子无法注水,其他的柱子注水时,当前柱子能注多少水取决于,他左边最大的柱子和右边最大的柱子的高度。
 * 如果当前柱子小于左右最大柱子时才可能注水
 * 最大注水=min(height[l_max],height[r_max])-height[current]
 */
     public int trap(int[] height) {
        int sum=0;
        for (int current=1;current<height.length-1;current++){
            int l_max=current-1,r_max=current+1;
            for (int l=current-1;l>=0;l--){
                if (height[l]>height[l_max])l_max=l;
            }
            for (int r=current+1;r<height.length;r++){
                if (height[r]>height[r_max])r_max=r;
            }
            int min_bar = Math.min(height[l_max], height[r_max]);
            if (min_bar>height[current]) {
                sum += min_bar - height[current];
            }
        }
        return sum;
    }

时间复杂度:On^2

空间复杂度:O1
在这里插入图片描述

解法3:左右夹逼

/**
 * 思路:
 * 从左到右,把每个柱子最多能注的水累加起来
 *
 * 记录左右柱子中最小的那个lower(地平线)
 * 比较当前地平线lower是否高于level(水库高度)
 * level-lower=当前柱子最大承载的水量
 */
    public int trap(int[] height) {
        int l=0,r=height.length-1,lower=0,level=0,water=0;
        while (r>l){
            lower=Math.min(height[l],height[r]);
            level=Math.max(level,lower);
            water+=level-lower;
        }
        return water;
    }

时间复杂度:On

空间复杂度:O1
在这里插入图片描述

解法4:栈

/**
 * 思路:
 * 核心思路:
 * 积水的面积 = 当前棒子和最低的那个棒子的高度差 * 他们的距离。
 *
 * 当前值小于栈顶元素就说明可能有积水产生,入栈
 * 如果当前值大于栈顶元素,说明积水产生,出栈当前栈顶元素
 * 比较栈顶和当前值的,找出小的那个min(积水产生的最大高度)。
 * 求出栈顶和当前元素的距离 *(min-出栈的元素值)
 */
     public int trap(int[] height) {
        ArrayDeque<Integer> stack = new ArrayDeque<>();
        int sum=0;
        for (int current=0;current<height.length;current++){
            while (!stack.isEmpty()&&height[current]>=height[stack.peek()]){
                Integer pop = stack.pop();
                if (stack.isEmpty())break;
                int distance=current-stack.peek()-1;
                int min=Math.min(height[stack.peek()],height[current]);
                int area=(min-height[pop])*distance;
                sum+=area;
            }
            stack.push(current);
        }
        return sum;
    }

时间复杂度:On^2

空间复杂度:On
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值