题解 | #容器盛水问题#

容器盛水问题

http://www.nowcoder.com/practice/31c1aed01b394f0b8b7734de0324e00f

对于每一个位置i,它上方所能容纳的水的容量等于:

Max{Min{i位置左侧的最大值,i位置右侧的最大值}-arr[i],0}

​ 而整个容器所能容纳的水的容量就为每一个位置上所能容纳的水的容量之和,第一个位置和最后一个位置上方所能容纳的水的容量为0。

​ 为此,要求两个辅助数组leftMax和rightMax。leftMax[i]表示arr[0..i-1]中的最大值,rightMax[i]表示arr[i+1,arr.length-1]中的最大值。

public long maxWater (int[] arr){

    if (arr == null || arr.length <= 2){
        return 0;
    }

    int n = arr.length;
    // leftMax[i]表示arr[i]左侧的最大值
    // rightMax[i]表示arr[i]右侧的最大值
    long[] leftMax = new long[n];
    long[] rightMax = new long[n];

    long left = arr[0];
    long right = arr[n-1];
    for (int i = 1; i < n; i++) {

        left = left >= arr[i-1] ? left : arr[i-1];
        leftMax[i] = left;
    }

    for (int i = n-2; i > 0; i--) {

        right = right >= arr[i+1] ? right : arr[i+1];
        rightMax[i] = right;
    }

    long res = 0;

    // 容器盛水的容量等于,对于每一个位置i的上方的容量之和,那么每一个位置上方的水容量为:
    // Max{Min{i位置左侧的最大值,i位置右侧的最大值}-arr[i],0}
    for (int i = 1; i < n - 1; i++) {
        res += Math.max(Math.min(leftMax[i],rightMax[i])-arr[i],0);
    }

    return res;
}

优化一下空间复杂度,将其由上面的O(n)变为O(1):

​ 设定左右两个指针i和j,初始值设置为1和n-2;然后再设置两个辅助变量leftMax和rightMax,分别表示i左侧的最大值和j右侧的最大值。那么求解的过程就是:

​ 如果leftMax<=rightMax,那么说明i位置的右侧的最大值一定不会小于rightMax,那么决定i位置处水容量大小的决定因素就是rightMax了,所以此时可以求解i位置容纳的水量:
Max{leftMax - arr[i],0}
然后更新leftMax,L向右移动:

leftMax = leftMax >= arr[i] ? leftMax : arr[i];
​ 对于leftMax>rightMax,也是同样的道理,说明R位置左侧的最大值一定不会小于leftMax,那么决定j位置处的水容量的大小的决定因素就是leftMax了,所以此时可以求解j位置的水量:

Max(rightMax-arr[j],0)
​ 然后更新rightMax,j向左移动:

rightMax = rightMax >= arr[j] ? rightMax : arr[j];
​ 最终的结果就是将i和j位置上的容量相加起来的结果。

    public long maxWater (int[] arr) {
        if(arr == null || arr.length <= 2)return 0;
        long res = 0;
        int n = arr.length;
        int leftMax = arr[0],rightMax = arr[n - 1];
        int i = 1,j = n - 2;
        while(i &lt;= j){
            if(leftMax &lt;= rightMax){
                res += Math.max(leftMax - arr[i],0);
                leftMax = leftMax &gt;= arr[i] ? leftMax : arr[i];
                i++;
            } else {
                res += Math.max(rightMax - arr[j],0);
                rightMax = rightMax &gt;= arr[j] ? rightMax : arr[j];
                j--;
            }
        }
        return res;
    }
全部评论

相关推荐

买蜜雪也用卷:我觉得应该没有哪个人敢说自己熟练使用git,代码分支一复杂还是得慢慢寻思一下的,不过基本的拉代码提交代码还有分支什么的是应该会
点赞 评论 收藏
分享
05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务