42.接雨水(Java)

题目描述

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

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:
在这里插入图片描述

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6


解法一:动态规划(未建DP table)

图解过程
在这里插入图片描述

  • 两边都高于当前列,较矮的一边减当前列即可
    在这里插入图片描述
    代码过程

  • 先找出右边最高的墙的索引。

  • 扫描过去。同时更新左边最高的墙

  • 当超过右边最高的墙时,左边最高墙变成右边最高墙,右边最高墙重新计算。

  • 依次下去直至扫描到结尾

class Solution {
    public int trap(int[] height) {
        int n=height.length;
        //获取最右最大值索引
        int maxIndex=getMaxIndex(height,0);
        int max=0,sum=0,tempsum=0;
        for(int i=0;i<n;i++){
            //若超出最右最大值索引,重置最大值及其索引
            if(i>maxIndex){
                maxIndex=getMaxIndex(height,i);
                max=height[maxIndex];
            }
            //更新起始节点
            if(height[i]>=max){
                sum+=tempsum;
                tempsum=0;
                max=height[i];
            }
            //较低的墙会积水
            if(height[i]<max){
                tempsum+=max-height[i];
            }
        }
        return sum;
    }
    public int getMaxIndex(int[] height,int start){
        int maxIndex=0,max=0;
        for(int i=start;i<height.length;i++){
            if(height[i]>=max){
                max=height[i];
                maxIndex=i;
            }
        }
        return maxIndex;
    }
}

在这里插入图片描述

代码优化,两次遍历
class Solution {
    public int trap(int[] height) {
        int n=height.length;
        //获取最右最大值索引
        int maxIndex=getMaxIndex(height,0);
        int max=0,sum=0,tempsum=0;
        //从左往右
        for(int i=0;i<maxIndex;i++){
            //更新起始节点
            if(height[i]>max){
                sum+=tempsum;
                tempsum=0;
                max=height[i];
            }
            //较低的墙会积水
            if(height[i]<max){
                tempsum+=max-height[i];
            }
        }
        max=0;
        //从右往左到达最大值
        for(int i=n-1;i>maxIndex;i--){
            if(height[i]>=max){
                sum+=tempsum;
                tempsum=0;
                max=height[i];
            }
            if(height[i]<max){
                tempsum+=max-height[i];
            }
        }
        return sum+tempsum;
    }
    public int getMaxIndex(int[] height,int start){
        int maxIndex=0,max=0;
        for(int i=start;i<height.length;i++){
            if(height[i]>=max){
                max=height[i];
                maxIndex=i;
            }
        }
        return maxIndex;
    }
}
优化代码,找最大值放一块
class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int tempmax=0,maxIndex=0;
        for(int i=0;i<n;i++){
            if(height[i]>=tempmax){
                tempmax=height[i];
                maxIndex=i;
            }
        }
        int max=0,sum=0,tempsum=0;
        for(int i=0;i<maxIndex;i++){
            if(height[i]>max){
                sum+=tempsum;
                tempsum=0;
                max=height[i];
            }
            if(height[i]<max){
                tempsum+=max-height[i];
            }
        }
        max=0;
        for(int i=n-1;i>maxIndex;i--){
            if(height[i]>=max){
                sum+=tempsum;
                tempsum=0;
                max=height[i];
            }
            if(height[i]<max){
                tempsum+=max-height[i];
            }
        }
        return sum+tempsum;
    }
}
解法二:动态规划(建立DP table)
class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int[] left_height=new int[n];
        int[] right_height= new int[n];
        for(int i=1;i<n;i++){
            left_height[i]=Math.max(left_height[i-1],height[i-1]);
        }
        for(int i=n-2;i>=0;i--){
            right_height[i]=Math.max(right_height[i+1],height[i+1]);
        }
        int sum=0;
        for(int i=1;i<n-1;i++){
            if(height[i]<left_height[i]&&height[i]<right_height[i]){
                sum+=Math.min(left_height[i],right_height[i])-height[i];
            }
        }
        return sum;
    }
}

在这里插入图片描述

优化,去掉一个数组
class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int left_max=0,sum=0;
        int[] right_max= new int[n];
        for(int i=n-2;i>=0;i--){
            right_max[i]=Math.max(right_max[i+1],height[i+1]);
        }
        for(int i=1;i<n-1;i++){
            left_max=Math.max(left_max,height[i-1]);
            if(height[i]<left_max&&height[i]<right_max[i]){
                sum+=Math.min(left_max,right_max[i])-height[i];
            }
        }
        return sum;
    }
}
优化为双指针
  • 哪边墙低,更新哪边墙
class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int left=1,right=n-2,sum=0;
        int left_max=0,right_max=0;
        while(left<=right){
            if(height[left-1]<height[right+1]){
                left_max=Math.max(left_max,height[left-1]);
                if(left_max>height[left]){
                    sum+=left_max-height[left];
                }
                left++;
            }
            else{
                right_max=Math.max(right_max,height[right+1]);
                if(right_max>height[right]){
                    sum+=right_max-height[right];
                }
                right--;
            }
        }
        return sum;
    }
}
解法三:栈
  • 当低于或等于栈顶,进入栈,cur++;
  • 高于栈顶,弹出栈顶,若栈非空,计算距离,sum+=距离* 两侧最低高度
  • 继续循环,直至栈为空或低于等于栈顶,跳出循环
    在这里插入图片描述
class Solution {
    public int trap(int[] height) {
        int n=height.length,cur=0;
        Stack<Integer> stack=new Stack<>();
        while(cur<n){
            while(!stack.isEmpty()&&height(cur)>height[stack.peek()]){
                int h=height[stack.peek()];
                stack.pop();
                if(stack.isEmpty()){
                    break;
                }
                int distance=cur-stack.peek()-1;
                int min=Math.min(height[stack.peek()],height[cur]);
                sum+=distance*(min-h);
            }
            stack.push(cur);
            cur++;
        }
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值