LeetCode第十一题: 盛最多水的容器 (Java)

题目:字符串转换整数

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。



输入:height = [1,1]
输出:1

题目解读:

当时读题目的时候也没明白什么意思,看了示例1的图片明白了。

容纳最多的水,是由有两条垂线中较短的一根垂线 与 两条垂线的距离决定。

换成数学语言来说,即求 蓝色区域的面积。设两条垂线对应的坐标是x = i,x = j。长度为h[i], h[j]。蓝色区域面积为 min(h[i],h[j]) * ( j - i )

第一想法求出所有两条垂线所组成的面积。然后找办法优化,当时想法既然面积只由较短的垂线和两条垂线的距离决定,那么我只要固定一条垂线,找到比这条垂线高且距离最远,那么就是当前固定的这条垂线所能构成最大的蓝色区域面积(即容纳最多的水)。

举个例子:i 是那条固定的较小的垂线,蓝色区域则是它所能构成的最大蓝色区域。要保证 i 是较小的那条。比求出所有的面积可以筛掉一些结果。

 但是写代码时发现了一些问题,我是从后向前找比固定的那根垂线长的垂线,比如倒数第三根垂线。他所构成最大面积是蓝色而不是红色区域。这种情况没考虑到两条垂线距离最大,后来我想到的解决办法是,每次循环在固定垂线左右两边找到比固定垂线长且距离最大的垂线。

 方法一:自己分析出来的

在暴力循环求出所有两条垂线容纳的水基础上,想通过一些条件减少循环,实际上好像并没比暴力循环优化多少。

    //把 height[i] 当作较短的,找比他高的 且 距离最长 计算面积
    public static int maxArea(int[] height) {
        //存放最大值
        int max = 0;
        //当前循环的最大值  即左右两边比较
        int max1 = 0;
        int len = 0;
        // 固定垂线  右边 比固定垂线长 的 两条垂线的距离
        int len1 = 0;
        //固定垂线  左边 比固定垂线长 的 两条垂线的距离
        int len2 = 0;
        for (int i = 0; i < height.length; i++) {
            int end = height.length - 1;
            int start = 0;
            //固定垂线右边,从最后一根循环
            while (height[i] > height[end] && end > i) {
                end--;
            }
            len1 = end - i;
            //固定垂线左边,从第一根循环
            while (height[i] > height[start] && start < i) {
                start++;
            }
            len2 = i - start;
            //比较左右两边,最长距离
            len = (len1 > len2) ? len1 : len2;
            max1 = len * height[i];
            max = (max > max1) ? max : max1;
        }
        return max;
    }

写文章时发现,可以再稍微优化一下。当 i 超过length一半时,优先找左边符合条件的垂线。当找比 height[i] 长且  i - start > length - i  ,那么不需要再执行右边找符合条件垂线的循环。这种思路还是有点问题,加上这个优化也快不了多少。

方法二:官方双指针

两条垂线作为左右指针,每次循环移动长度较小的垂线,计算面积和比较,存储最大面积。因为所容纳最多的水是由较小的垂线决定。

 //双指针  每次循环扔掉较小的
    public static int maxArea2(int[] height) {
        int start = 0;
        int end = height.length - 1;
        int max = 0;
        while (start < end) {
            if (height[start] < height[end]) {
                max = (max > height[start] * (end - start)) ? max : height[start] * (end - start);
                start++;
            } else {
                max = (max > height[end] * (end - start)) ? max : height[end] * (end - start);
                end--;
            }
        }
        return max;
    }

相比我自己的想法,他相当于每次把较小的一个垂线计算完,就让掉了,不再参与后面循环,优化更大。

方法三:评论区看到的方法

评论区看到的,感觉也肯巧妙。他把 height[i] 当作固定的。我们每次循环都存储到当前最大面积,较小垂线长度 = 面积 / 边长(即两条垂线的距离),当前循环最大边长为 lengh-1-i (最后一根垂线不在用循环,他右边没有垂线了)。当 max / lengh-1-i > hight[ i ]时,表示当前循环的 height[i] 比 目前存储最大面积所需要的最小垂线还短,所以他一定不是符合条件的垂线,他不再需要进入循环面积比较。

 public static int maxArea(int[] height) {
        int len = height.length;
        int max = 0, water = 0;
        for(int i = 0 ; i < len - 1; i++){
            // 每次循环左边固定height[i]
            //max / (len-i-1)  最大面积是,需要最小边的高。此时长度是最大,height[i]比需要最大面积最小边还小
            if(height[i]  > max / (len-i-1)){
                //面积比较
                for(int j = i + 1; j < len; j++){
                    water = (j-i) * Math.min(height[i], height[j]);
                    if(max < water)
                        max = water;
                }
            }
        }
        return max;
    }

总结:

这道题感觉并不难,再不济求出所有的面积进行比较。

做这道题时感觉很像小学时做的应用题,可以用很多办法解决问题,最后都可以得出答案。例如这道题的双指针,双指针单拿出来谁都会,但是应有到具体的问题上却想不起来。

最后做题不单单是为了最后得出答案,而是扩展思路,多种方法解决问题,这样面对其他类似的题目可以找到最优的解决办法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值