力扣热门100题——盛水最多的容器(暴力解法,双指针,贪心)

4、盛水最多的容器

1.问题描述

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

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

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

说明:你不能倾斜容器。

2.示例

示例 1:

img

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

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

3.提示

n == height.length
2 <= n <= 10^5
0 <= height[i] <= 10^4

4.具体解法(暴力解法,双指针,贪心)

//方法一:暴力解法
//第一反应是,找两个最长的板子,那之间的水不就是最多,但示例的例子就可以提醒自己,这么想是不对的
//那么就想到暴力遍历的方式
//因为两块板子之间的盛水量,就是两块板子的距离乘上低板子的高度
//所以弄上一个双层for循环,做i和j的差值(不用再多减一,九个线,数组下标最大值正好是8,8-0正好是最大的距离8)
//差值乘height[i]和height[j]中的小者,存储到一个最大值变量中,最后返回即可。
//附上自己写的暴力循环,超出时间限制
//发现很多超出时间限制的,问题就在于,二层循环有很多冗余的操作,一般都是从这里入手去解决相关的问题
//自己应该去找到一个解决的思路,其实之前已经有了,只不过自己没去细致的掌握,应该掌握一下,可以做到一通百通,起码在暴力这一块,可以做到能用就能通过
/*
class Solution {
    public int maxArea(int[] height) {
        int maxsum=0;//最后返回的最大值
        int nowsum=0;//临时的最大值
        int len=height.length;
        for(int i=0;i<len-1;i++){
            for(int j=i+1;j<len;j++){
                if(height[i]<height[j]){
                    nowsum=height[i]*(j-i);
                }
                else{
                    nowsum=height[j]*(j-i);
                }
                if(nowsum>maxsum){
                    maxsum=nowsum;//这里我想直接在那个答题界面用max方法,但是会报错,应该还是得导包才可以用
                                  //好像是因为我没有用类名直接调用,我直接写的方法名,少写了类名
                }
            }
        }
        return maxsum;

    }
}
 */

//暴力解法的改进
//解决办法就是对height[i]进行一个判断,只有大的才有机会留下,小的不可能容量更大了,所以直接都不判断了
/*
class Solution {
    public int maxArea(int[] height) {
        if(height.length==1||height.length==0){
            return 0;
        }
        int max=0;
        for(int i=1;i<height.length;i++){
            if(height[i]>max/i)//注释掉这一句就会最后超时
                               //然后我把这一句给改成加个{}将后面的for包裹起来,也可以不超时
                               //我这样改的意义其实就是,如果这个height[i]比上一个更小,那肯定最大值还没有前面的那个做边的时候更大,直接跳过
            for(int j=0;j<i;j++){
                if(i==j)
                    continue;
                max=Math.max(max,(i-j)*Math.min(height[j],height[i]));
            }
        }
        return max;
    }
}

 */


//方法二:双指针
//这个思路我隐约也有感觉了,但是并没有完整的想出来
//他的核心思路就是两个指针从两端,计算盛水量,然后小的指针向中间移动一位,再计算盛水量,并更新最大值,以此类推
//这个原理可行的依据在于:双指针代表的是 可以作为容器边界的所有位置的范围。
//在一开始,双指针指向数组的左右边界,表示 数组中所有的位置都可以作为容器的边界,因为我们还没有进行过任何尝试。
//在这之后,我们每次将 对应的数字较小的那个指针往另一个指针的方向移动一个位置,就表示我们认为 这个指针不可能再作为容器的边界了。
//而为什么数字较小的那个指针不可能再作为容器的边界了?
//因为我们假设左右指针的高度是x和y,之间的距离是t,(x<y),那么容量应该是x*t,如果是右指针移动,那么无论怎样,容量都不会大于这个x*t了
//因为t会变小,而短板效应决定容器的高度最大也就是x了,所以一定不会变大了
//所以说,这个左指针作为边界的最大值我们已经找到了,而且后面不需要再用它作为边界了,那么自然就应该可以舍弃他了
//而且舍弃之后,就相当于回到最一开始指向左右边界一样,是一个新的左右边界,所以可以继续像前面一样考虑问题
/*
public class Solution {
    public int maxArea(int[] height) {
        int l = 0, r = height.length - 1;//定义左右指针
        int ans = 0;
        while (l < r) {
            int area = Math.min(height[l], height[r]) * (r - l);
            ans = Math.max(ans, area);
            if (height[l] <= height[r]) {
                ++l;
            }
            else {
                --r;
            }
        }
        return ans;
    }
}
 */
//同样的双指针思路,下面的这个击败百分之98,2ms
//而上面官方的双指针,击败百分之12,5ms
/*
class Solution {
    public int maxArea(int[] height) {
        int len=height.length;
        //i,j : 将指针指向数组两侧
        int i=0;
        int j=len-1;
        //res 计算出此时的储水量
        int res=Math.min(height[i],height[j])*(j-i);
        //给出扫描停止条件:i=j
        while(i<j){
            //找出较小的高度,将对应指针向内侧移动,直到找出大于原来高度的
            if(height[i]<=height[j]){
                int temp=i;
                while(height[i]<=height[temp] && i<j){
                    i++;

                }
                res=Math.max(res,Math.min(height[i],height[j])*(j-i));
            }else{
                int temp=j;
                while(height[j]<=height[temp] && i<j){
                    j--;
                }
                res=Math.max(res,Math.min(height[i],height[j])*(j-i));
            }

        }
        return res;
    }
}
 */

//下面这个我没运行,但是代码真的太简洁了
//也是双指针,用这个三目运算符,整个代码简洁明了
/*
class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length - 1, res = 0;
        while(i < j) {
            res = height[i] < height[j] ?
                    Math.max(res, (j - i) * height[i++]):
                    Math.max(res, (j - i) * height[j--]);
        }
        return res;
    }
}
 */

5.收获

  • 发现很多超出时间限制的,问题就在于,二层循环有很多冗余的操作,一般都是从这里入手去解决相关的问题,自己应该去找到一个解决的思路,其实之前已经有了,只不过自己没去细致的掌握,应该掌握一下,可以做到一通百通,起码在暴力这一块,可以做到能用就能通过。自己试着去想了一下找二层循环的冗余部分,发现不是很容易就能想到呢
  • 双指针的思路好像也是经常使用的,可以避免一些重复的操作
  • max方法的调用时类名直接调用,静态类嘛。Math.max(a,b)即可
  • 即使是同样的算法,还能有不小的差别,其中的原因还是没有掌握,但是应该是跟底层的逻辑结构有关系
  • 对于双指针的解法,有贪心的说法,就是每一次都是把短的舍弃,这种贪心的做法,只有移动短的,才可能容量变大,思路都一样,就是说法不一样
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值