42.接雨水

1.题目描述:

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

方法一:暴力解法。(按列计算)

1.主要思想:一列一列的遍历,计算每一列接的雨水。这一列接的雨水由这一列的左端最大高度和后端的最大高度的较小值决定。所以就需要计算左端的最大高度和后端的最大高度。

2.我们可以发现计算左端的最大高度很好解决,每次需要计算的列数往后走一格,左端就加一列,只需要判断加的这一列跟之前的最大值比较就好。但是右端是每次减少一列,这就不好判断。(解决右端就是后面说到的双指针法,大大提高了效率。)

3.代码:

class Solution {
    public int trap(int[] height) {
    int sum = 0;
    for(int i = 1;i < height.length-1; i++){
    int left = 0; int rigth = height.length-1;
    int maxleft = 0; int maxrigth = 0;
     while(left <= i-1){
         if(height[left] >= maxleft){
             maxleft = height[left];
         }
         left++;
     }
     while(rigth >= i+1){
         if(height[rigth] >= maxrigth){
             maxrigth = height[rigth];
         }
          rigth--;
     }
     int temp = Math.min(maxleft,maxrigth);
     if(temp > height[i]) sum = sum + temp - height[i];
    }
    return sum;
    }
}

方法二、单调栈方法(按行和列同时来计算接水) 

1.思路描述:使用单调栈,栈是从小到大排序的,每次出栈就需要计算出栈的该列某几行的接雨水数。现在需要解决的是什么时候出栈。以及每次出栈计算的是哪几行的雨水。

2.什么时候出栈:出栈就是需要计算该列接的部分雨水,该列可以接雨水的条件就是产生凹槽,产生凹槽就是当遍历到一列入栈时大于栈口的数据,说明右边有一个比该列高的数据,那么本身是单调栈,那么左边也存在一个高的数据,那么就产生了凹槽。

3.出栈计算的是该列所有的雨水还是部分:首先大方向是,当遇到一个较大数据时需要不断出栈,知道入栈,入一个栈需要计算的是在这个列之前可以接的雨水。那么出栈计算的该列的是哪一行的雨水呢,每一次出栈就是计算左右凹槽较小的高度到该列中间行的雨水接的雨水,算完这几行后就可以看作已经填满了。

 代码:

class Solution {
    public int trap(int[] height) {
    Stack<Integer> stack = new Stack<Integer>();
    int sum = 0;
    for(int i = 0; i < height.length; i++){
        while(!stack.isEmpty() && height[i] > height[stack.peek()])
       {//一次入栈操作
           int temp = stack.pop();
           if (stack.isEmpty()) break; 
           int distance = i - stack.peek() - 1;
           int height1 = Math.min(height[i],height[stack.peek()]) - height[temp];
           sum = sum + distance * height1;//出栈一次接的雨水量
       }
        stack.push(i);
    }
    return sum;
    }
}

方法三、双指针法

1.思路描述:大致跟方法一的暴力解法相似。但是我们可以发现计算左端的最大高度很好解决,每次需要计算的列数往后走一格,左端就加一列,只需要判断加的这一列跟之前的最大值比较就好。但是右端是每次减少一列,这就不好判断。(解决右端就是后面说到的双指针法,大大提高了效率。)

2.通过双指针法就是2个指针分别从头和尾进行每列的雨水的接的量。左列的左边最大值Lleftmax很好求,计算尾部的列的右边Rrightmax的最大值也很好求。现在需要解决的是左列的右边Lrightmax的最大值和尾部列的左边Rleftmax的最大值。下面会通过分析发现后者根本需要计算。

通过下面的代码分析更加清晰:重点分析:height[left - 1] < height[right + 1]的时候计算左列的雨水量,当小于的时候我们可以理解遍历左侧的列找到一个比右边尾部列要大的数,如果没找到说明左边的最大值肯定要小于尾部的数,接的雨水肯定是由最小的决定,所以可以直接计算出结果。当找到一个该数时,left-1是第一个大于尾部的数所以也就是左边最大的数。同理右边也一样。

 for (int i = 1; i < height.length - 1; i++) {

        //从左到右更

        if (height[left - 1] < height[right + 1]) {

            max_left = Math.max(max_left, height[left - 1]);

            int min = max_left;

            if (min > height[left]) {

                sum = sum + (min - height[left]);

            }

            left++;

        //从右到左更

        } else {

            max_right = Math.max(max_right, height[right + 1]);

            int min = max_right;

            if (min > height[right]) {

                sum = sum + (min - height[right]);

            }

            right--;

        }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值