力扣 11. 盛水最多的容器

题目描述:

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

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

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

说明:你不能倾斜容器。

示例 1:

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

题解1:

我的思路,首先双重for肯定超时,但是又没有想到好的办法,因此基于最直接的双层for下进行剪枝,遍历0号元素的时候(假设高度h,下标是x),我先计算其与len-1号元素(假设高度h1,下标是x1)形成的容器容量,之后开始剪枝;

假设现在h=<h1,现在容量是h*(x1-x)  那么x到x1之间的全部元素都不需要遍历,因为由x1移到x1-1位置时,相当于宽减小,高最大也还是h,即容量是h*(x1-1-x),也就是说x到x1之间的每一个容器容量都不会大于x和x1形成的容器容量,因此只要h<h1时,既不需要计算x到x1之间的值了。

假设现在h>h1,现在容量是h1*(x1-x) 那么当x到x1之间的某个位置(假设是x1-1)高度(假设是h2)大于h1时才有必要计算,因为如果小于h1,现在计算出来的容量相当于高度和宽度都减小了,即

h2*(x1-1-x),因此这种情况也没必要计算了。

至此完成剪枝。

说明:55/63,其实剪枝没减成功,本质还是双重for,只有一点点用。而且因为使用stream流,导致开销还增大了,所以这段代码就是史,没有什么参考价值,只是里面的stream流可以学习一下。

代码实现:

public static void main(String[] args) {
        int a[] = new int[]{2,3,4,5,18,17,6};
        System.out.println(maxArea(a));
    }
        public static int maxArea(int[] height) {
            //第一遍遍历找到所有大于最后一个位置高度的数组元素,将其下标和对应的高度放入
            Map<Integer,Integer> map = new HashMap<>();
            int len = height.length;
            for(int i =0;i<len-1;i++){
                if(height[i]>height[len-1]){
                    map.put(i,height[i]);
                }

            }
            //System.out.println("输出map大小: "+map.size());



            double maxSize= 0;
            int k = 0;
            for(k=0;k<len;k++){
                if(height[k]>height[len-1]){
                    maxSize = Math.max(maxSize,height[len-1]*(height.length-1-k));
                    final int kk = k;
                    //计算出来大于最后一块木板高度的木板们可能在当前左板(k)的左边,此时就不需要计算了,map中过滤出来的就是还在k右边的木板们
                    Map<Integer, Integer> tempMap = map.entrySet().stream()
                            .filter(entry -> entry.getKey() > kk)
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                    //System.out.print("第"+kk+"次遍历tempMap的大小: "+tempMap.size()+" ");
                    if(tempMap.size()>0){

                        for (Integer key : tempMap.keySet()) {
                           int h2 = tempMap.get(key);
                           int h = height[k];
                           if(h2>=h){
                               maxSize = Math.max((h*(key-k)),maxSize);
                           }else{
                               maxSize = Math.max(maxSize,(h2*(key-k)));
                           }
                        }
                    }
                }
                else{
                    maxSize = Math.max(maxSize,height[k]*(height.length-1-k));
                }
               //System.out.println("第"+k+"次遍历完的最大值: "+maxSize);
            }
            return (int)maxSize;
        }

新知识点1:使用stream流来过滤map中满足key>kk的k-v对,并且将其过滤出来的k-v对从stream中过滤出来收集到一个新map中

  Map<Integer, Integer> tempMap = map.entrySet().stream()
                            .filter(entry -> entry.getKey() > kk)
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

需要注意的是,kk必须是一个final类型

题解2:

看了官方题解,使用双指针,需要有数学敏感度,不然想不出来。

思路是,指针L指向首元素,R指向尾元素,计算出容量,然后heigeht[L]和height[R]中较小的一个往中间移动一位,为什么?因为没移动之前的高度受较小的L和R中的较小的一个限制,移动了较小的一个之后,可能现在的高度就会增加,因此计算出来的容量也就可能会增加,尽管宽度减小了,但是高度可能会增加,乘积也就可能增大,是找出最大容量的正确办法;

但是如果现在将L和R中高度较大的一个向中间移动了,那么新容器的高度不会超过之前L和R高度的较小值h(不管移动之后得到新木板高度是多少,容器的新高度还是受制于新左右木板的较小值),并且宽度一定减小,从而容器容量一定小于等于之前容量,因此移动了LR中较大值后,后续的计算全部都无效,对于找出最大容量毫无作用了,也就是说以当前LR中较小值为边界的所有情况中,我们已经计算出来最大容量的值,其他任何固定较小值,移动较大值形成的新容器的容量都不会比当前LR形成的容器容量大了,因此,以该较小值为边界的情况都可以不考虑,因此移动较小值,就可以做到不重不漏的计算判断。

代码实现:

class Solution {
    public int maxArea(int[] height) {
        int maxSize=0;
        int l = 0;
        int r = height.length-1;
        while(l<r){
                maxSize = Math.max(Math.min(height[l],height[r])*(r-l),maxSize);
            if(height[l]<height[r]){
                l++;
            }else{
                r--;
            }
        }
        return maxSize;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值