图解LeetCode——11. 盛最多水的容器(难度:中等)

一、题目

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

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

说明:你不能倾斜容器。

二、示例

2.1> 示例 1:

图解LeetCode——11. 盛最多水的容器(难度:中等)_第1张图片

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

2.2> 示例 2:

【输入】height = [1,1]
【输出】1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

三、解题思路

3.1> 思路1:双向指针

通过题意,我们会接收到一个整数数组height,它里面有n个数值,代表木桩的高度。任意两个木桩都可以与x轴组成一个容器水槽,那么哪个组合的水槽可以容纳更多的水呢?其实这道题里面有两个因素是盛水容量的关键点:一个是水槽x轴的长度,另一个就是两个木桩中最短的木桩高度。这两个值的乘积就是盛水容量的大小了。

那么双向指针的解法就是创建两个指针——head头指针tail尾指针。最初head指向height数组中index=0的位置,tail指向height数组中index=height-1的位置。那么此时情况假设我们如下图所示,height[head]是小于height[tail]的,head指针与tail的指针长度为x(即:tail - head = height - 1 - 0),那么此时水槽可以承载的水容量就等于:height[head] * x。如下图所示:

图解LeetCode——11. 盛最多水的容器(难度:中等)_第2张图片

那么根据双指针算法,我们需要让head指针或者tail指针向彼此靠拢,那么移动哪一个指针的?我们会根据对比两个木桩的高度,去移动那个更短的。比如,head指针指向的木桩较短,那么我们将head指针向右移动。但是如果是tail指针指向的木桩较短,那么我们就将tail指针向左移动。当两个指针在某个位置相遇,则结束整个过程。

那么有同学会问,为什么要这么对比呢?这道题,我们一般来说,第一反应应该是两层for循环,即,先锁定第一个木桩,然后去依次对比其他的木桩,每次计算容量,通过Math的max函数,只记录最大容量的。然后我们再锁定第二个木桩,再去对比其他的木桩(非第一个木桩),然后一次类推。这种方式是没错的,但是属于暴力破解的范畴了。那么,双向指针能保证计算的正确性吗?

我们还是以上面图为例,由于最初的head指向的木桩高度为height[0],tail指向的木桩高度为height[height.length - 1],他们之间的距离为x,由于height[0] 小于 height[height.length - 1],所以容器的盛水容量就是 height[0] * x,这里的x已经是最大值的,也就是说,无论head指针和tail指针怎么移动,两个指针之间的距离都不会大于x。那么其实我们就相当于“锁定”了最大容器底部x了,而每次无论移动head指针还是tail指针,对于容器底部x的影响,都是一直在减小的,那么它的变化是一个恒定减小的趋势。而在容器高度方面,确是一个变化的,因为我们并不知道到底是head指向的木桩高,还是tail指向的木桩高。

那么此时,我们第二个问题就是,怎么移动木桩呢?为什么要移动较短的呢?通过上面我们得出的公式height[head] * x(前提是head的木桩高度小于tail的木桩高度),那么假设我们移动更高的木桩——即tail指针,那么容器底部为:x - 1,由于没有移动较短的木桩height[head],所以无论tail指针全新指向的木桩高度是多高,总的容量高度都不会超过height[head]的高度,那么最大可能性容量就是:height[head] * (x - 1);那么此时如果我们移动更矮的木桩——即head指针,那么容器底部为:x - 1,由于没有移动较高的木桩height[tail],所以无论head指针全新指向的木桩高度是多高,总的容量高度都不会超过height[tail]的高度,那么最大可能性容量就是:height[tail] * (x - 1);那么由于head < tail,所以自然就得出了height[head] * (x - 1) 的结论。而本题是要获取最大盛水容量,所以,自然就要移动较矮的木桩了。具体如下图所示:

图解LeetCode——11. 盛最多水的容器(难度:中等)_第3张图片

那么上面我们介绍了如何去计算最大盛水容量,下面就以题目中的第一个示例1为例,通过每一步骤执行的图解方式,完整的展现一次执行的全过程。那么示例1中,height数组中的元素为[1,8,6,2,5,4,8,3,7],我们依次通过移动head指针或者tail指针,并且计算出每次的“最大容量” ,并通过Math的max函数,将最终的最大容量作为结果进行返回。下面是具体的8个执行步骤,如下所示:

图解LeetCode——11. 盛最多水的容器(难度:中等)_第4张图片

图解LeetCode——11. 盛最多水的容器(难度:中等)_第5张图片

四、代码实现

4.1> 实现1:双向指针

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

图解LeetCode——11. 盛最多水的容器(难度:中等)_第6张图片

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ (^o^)/ ~ 「干货分享,每天更新」

往期推荐

题目来源:力扣(LeetCode)

你可能感兴趣的:(算法,leetcode,算法,面试)