题目描述
思路解析
一句话答案:LeetCode 11 盛最多水的容器的最优解是对撞双指针:左右指针从两端出发,每轮用 min(左高, 右高) × 宽度更新最大面积,然后移走较矮的一端——矮边是水位瓶颈,留着它宽度只会更小、面积注定更差。时间 O(n)、空间 O(1),比枚举所有线对的 O(n²) 快一个量级。
这道题真正在问什么
n 条垂线立在 x 轴上,第 i 条高 height[i],选两条和 x 轴围成容器,求最大盛水量。面积公式是理解一切的起点:水位取决于较矮的一条——高的那条再高,水也会从矮边溢出——所以面积 = min(height[l], height[r]) × (r - l)。题目要的就是这个乘积在所有线对里的最大值。
为什么暴力枚举是 O(n²),瓶颈在哪
枚举所有线对共约 n²/2 个,逐个算面积取最大,就是 O(n²)。慢在大量明显没前途的组合也要逐个算一遍,比如两条都很矮、又靠得近的线对。要提速,需要一个能一次排除一整批组合的判断依据——这个依据就藏在「水位由矮边决定」这条性质里。
为什么每次必须移走较矮的那端
让左右指针从最两端开始——这是宽度最大的组合。假设当前左边较矮,考虑所有「保留这条矮左边」的其他组合:右指针只能往里收,宽度必然变小;而水位仍被这条矮左边封顶,min 不可能超过它的高度。宽度变小、水位不涨,面积只会更差。也就是说,这条矮边与最远端配对的当前面积,已经是它能参与的所有组合中的最优——它的潜力被榨干了,可以放心弃掉,把指针向内移动。
这就是对撞双指针每一步的正确性依据:每轮弃掉的不是「一个位置」,而是「以该矮端为一边的全部剩余组合」,一步排除一批。n 步之后,所有可能成为最优的组合都被检查过,因此不会漏解,这也是复杂度能从平方降到线性的根本原因。
两端一样高时该移哪边
相等时随便移哪端都对。此时水位被这个相等的高度封顶,无论保留哪一端往里收,宽度都在缩、min 都不会超过当前值,两侧的剩余组合都不可能超过当前面积——而当前面积已经记录在案,之后怎么移都不会漏掉更优解。实现里写 height[l] < height[r] 时移左指针、否则移右指针即可,不需要为相等单独特判。
复杂度怎么算,容易错在哪
两个指针合计从两端走到相遇,每轮只做常数次计算,总时间 O(n);只用 l、r、best 三个变量,空间 O(1)。最常见的错误有两个:一是用较高的一边算水位——水从矮边溢出,必须取 min,取 max 会高估面积;二是凭感觉固定移某一侧的指针——移动依据只能是「谁矮移谁」,方向错了上面的排除论证就不成立,会漏掉真正的最优组合。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
双指针的诀窍:从最两端(最宽)起,每次只移走「较矮」的一边——移矮端才有翻盘机会;留着它,宽度还在缩、瓶颈还是它,面积注定更差。
初始:l 指向最左(下标 0,高 1),r 指向最右(下标 8,高 7)。从最宽的容器开始往里收。 宽度最大,但水位被矮的一边压着。
看这对线:左下标 0(高 1)、右下标 8(高 7),间距 = 8−0 = 8。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(1,7) = 1。面积 = 1×8 = 8。 刷新最大面积 → 8!
左端较矮(高 1 < 右端 7)。移走它 → l 进到下标 1。留着这根矮柱:宽度只会更小、瓶颈还是它,面积注定更差;移走才有翻盘机会。
看这对线:左下标 1(高 8)、右下标 8(高 7),间距 = 8−1 = 7。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,7) = 7。面积 = 7×7 = 49。 刷新最大面积 → 49!
右端较矮或持平(高 7 ≤ 左端 8)。移走它 → r 退到下标 7。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 7(高 3),间距 = 7−1 = 6。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,3) = 3。面积 = 3×6 = 18。 没超过当前最大 49。
右端较矮或持平(高 3 ≤ 左端 8)。移走它 → r 退到下标 6。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 6(高 8),间距 = 6−1 = 5。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,8) = 8。面积 = 8×5 = 40。 没超过当前最大 49。
右端较矮或持平(高 8 ≤ 左端 8)。移走它 → r 退到下标 5。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 5(高 4),间距 = 5−1 = 4。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,4) = 4。面积 = 4×4 = 16。 没超过当前最大 49。
右端较矮或持平(高 4 ≤ 左端 8)。移走它 → r 退到下标 4。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 4(高 5),间距 = 4−1 = 3。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,5) = 5。面积 = 5×3 = 15。 没超过当前最大 49。
右端较矮或持平(高 5 ≤ 左端 8)。移走它 → r 退到下标 3。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 3(高 2),间距 = 3−1 = 2。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,2) = 2。面积 = 2×2 = 4。 没超过当前最大 49。
右端较矮或持平(高 2 ≤ 左端 8)。移走它 → r 退到下标 2。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
看这对线:左下标 1(高 8)、右下标 2(高 6),间距 = 2−1 = 1。能盛多少,要看较矮的那条。
水位受限于较矮的一边 min(8,6) = 6。面积 = 6×1 = 6。 没超过当前最大 49。
右端较矮或持平(高 6 ≤ 左端 8)。移走它 → r 退到下标 1。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
两指针走到这里,最优容器锁定:左下标 1(高 8)、右下标 8(高 7),宽 7、水位 7 → 面积 49。
边界先想清:至少两条线,答案取决于「矮边高 × 宽」的最佳组合。
面试最常被追问的两点。
参考代码
def maxArea(height): l, r = 0, len(height) - 1 best = 0 while l < r: area = min(height[l], height[r]) * (r - l) best = max(best, area) if height[l] < height[r]: l += 1 else: r -= 1 return best复杂度
- 时间:O(n),l 和 r 合计向中间走一遍
- 空间:O(1),只用 l/r/best 几个变量
易错点
面试追问把动画讲成自己的话
追问移走较矮端,会不会漏掉更优的解?
追问为什么暴力是 O(n²),这里能降到 O(n)?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
接雨水
LeetCode 42 · 困难 · 沿着 双指针 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题