携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情 >>
一、题目
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水,返回容器可以储存的最大水量。
说明:你不能倾斜容器。
二、示例
2.1> 示例 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
。如下图所示:
那么根据双指针算法,我们需要让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) <height[tail] * (x - 1)
的结论。而本题是要获取最大盛水容量,所以,自然就要移动较矮的木桩了。具体如下图所示:
那么上面我们介绍了如何去计算最大盛水容量,下面就以题目中的第一个示例1为例,通过每一步骤执行的图解方式,完整的展现一次执行的全过程。那么示例1中,height数组中的元素为[1,8,6,2,5,4,8,3,7],我们依次通过移动head指针或者tail指针,并且计算出每次的“最大容量” ,并通过Math的max函数,将最终的最大容量作为结果进行返回。下面是具体的8个执行步骤,如下所示:
四、代码实现
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;
}
}
今天的文章内容就这些了:
写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。
更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ (^o^)/ ~ 「干货分享,每天更新」
往期推荐
LeetCode精讲——1252. 奇数值单元格的数目(难度:简单)
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。