接雨水问题

接雨水问题:

一.问题分析

  刚开始看到题目的时候

再多看几眼还是没什么头绪,于是试着从左到右一点一点推到下去。

 

  1. 首先数组的第一个和最后一个元素有一边没有边界,所以这两个不用考虑,只考虑从1到n-2个元素。

  2. 2装不了水,因为左边没有柱子挡着;3可以装水,因为两边有柱子挡着,而且两边的柱子都比它高;4装不了水,原因是虽然它两边有柱子,但是因为2比它矮;5能装水取决于4和8的两边,能装多少取决于比较矮的4,具体装水多少直接用4的高度减去5的高度为1........

有了上面的推导过程我们可以得出:

  • 如果某一个节点能装水,那么他一定存在左右两边柱子挡着,而且左右两边的柱子都要比它高。

  • 根据常识,某一个节点装水多少取决于它的两边最高柱子当中比较矮的柱子,再减去这个节点的高度就可以了。

二.暴力破解

  根据分析,我们可以采用暴力解法,从1到n-2遍历元素,每遍历一个元素就算出它左右两边的最高柱子left_max和right_max出来,再取left_max和right_max较小的一个元素,如何这个元素比当前柱子高,直接减去当前柱子的高度就可以了,代码如下:

public class YuShui {

    public static int trap(int[] height) {
        int sum=0;  //要求的值
        //开始遍历
        for(int k=1;k<height.length-1;k++){
            int left_max=0;
            //开始遍历左边找到最高值
            for(int l=k-1;l>=0;l--){
                if(height[l]>left_max){
                    left_max=height[l];
                }
            }
            int right_max=0;
            //开始遍历右边找到最高的
            for(int r=k+1;r<height.length;r++){
                if(height[r]>right_max){
                    right_max=height[r];
                }
            }
            int temp=left_max>=right_max?right_max:left_max;
            if(temp>height[k]){ //
                sum=sum+(temp-height[k]);
            }
        }
        return sum;
    }

    public static void main(String[] args) {
        int[] height={0,1,0,2,1,0,1,3,2,1,2,1};
        System.out.println(trap(height));
    }
}

 

提交LeeCode成功:时间复杂度: O(n^2),空间复杂度:O(1)

二.动态规划

 上面的算法之所以性能差,是因为每一次求某个位置i的左边最高位置left_max和右边最高位置right_max的时候都要从头计算,多了很多重复计算的东西。那我们可以把以前计算过的结果用数组存起来,用记忆化搜索来解这道题:

定义数组: left_max[]:表示某一个柱子左边的最高的(不包含本身);

               right_max[]:表示某一个柱子右边的最高的(不包含本身);

于是就可以得出状态转移方程: left_max[i]=max(left_max[i-1],height[i-1]);注:因为left_max[i-1]不包含height[i-1]进去,所以还要和height[i-1]比较取较大的。

同样:right_max[j]=max(right_max[j+1],height[j+1]);

代码如下:

public class YuShui1 {

    public static int trap(int[] height) {
        int sum=0;
        int [] left_max=new int[height.length];
        int [] right_max=new int[height.length];
        //算出从1到n-1的左边最高点
        for(int l=1;l<height.length-1;l++){
           // left_max[l]=Math.max(left_max[l-1],height[l-1]);
      left_max[l]=left_max[l-1]>=height[l-1]?left_max[l-1]:height[l-1];
        }
        //算出从n-1到1的右边最高点
        for(int r=height.length-2;r>=0;r--){
           // right_max[r]=Math.max(right_max[r+1],height[r+1]);
    right_max[r]=right_max[r+1]>=height[r+1]?right_max[r+1]:height[r+1];
        }
        //开始循环计算
        for(int k=1;k<height.length-1;k++){
            int temp=left_max[k]>=right_max[k]?right_max[k]:left_max[k];
            if(temp>height[k]){
                sum=sum+(temp-height[k]);
            }
        }
        return sum;
    }

    public static void main(String[] args) {
        int [] height={0,1,0,2,1,0,1,3,2,1,2,1};
        System.out.println(trap(height));
    }

}

提交LeeCode之后的结果好了很多:时间复杂度:O(n),空间复杂度:O(n)

 

后来去官方看题解还要能牛逼的双指针法,他的时间复杂度O(n),空间复杂度O(1),不好意思,想不到。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值