盛最多水的容器 图解题解
两端往中间夹,每次只移矮的那根——矮板是瓶颈,留着它宽度还会缩,只移矮的才有机会翻盘。
两个人各举一块挡板站在数轴两端,往中间走,每步只挪矮的那个——因为短板决定水位,把高的挪进来只会让宽度缩小又不提水位,只有挪矮的才有机会换到更高的板子把水量翻盘上去。
这道题到底在问什么
- 输入
- height=[1,8,6,2,5,4,8,3,7]
- 输出
- 49
最优解:为什么这么做
一句话答案: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 会高估面积;二是凭感觉固定移某一侧的指针——移动依据只能是「谁矮移谁」,方向错了上面的排除论证就不成立,会漏掉真正的最优组合。
▶ 动画逐步走查(共 27 步)——想跟着动画一帧帧对照就展开
- 3双指针的诀窍:从最两端(最宽)起,每次只移走「较矮」的一边——移矮端才有翻盘机会;留着它,宽度还在缩、瓶颈还是它,面积注定更差。
- 4初始:l 指向最左(下标 0,高 1),r 指向最右(下标 8,高 7)。从最宽的容器开始往里收。 宽度最大,但水位被矮的一边压着。
- 5看这对线:左下标 0(高 1)、右下标 8(高 7),间距 = 8−0 = 8。能盛多少,要看较矮的那条。
- 6水位受限于较矮的一边 min(1,7) = 1。面积 = 1×8 = 8。 刷新最大面积 → 8!
- 7左端较矮(高 1 < 右端 7)。移走它 → l 进到下标 1。留着这根矮柱:宽度只会更小、瓶颈还是它,面积注定更差;移走才有翻盘机会。
- 8看这对线:左下标 1(高 8)、右下标 8(高 7),间距 = 8−1 = 7。能盛多少,要看较矮的那条。
- 9水位受限于较矮的一边 min(8,7) = 7。面积 = 7×7 = 49。 刷新最大面积 → 49!
- 10右端较矮或持平(高 7 ≤ 左端 8)。移走它 → r 退到下标 7。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 11看这对线:左下标 1(高 8)、右下标 7(高 3),间距 = 7−1 = 6。能盛多少,要看较矮的那条。
- 12水位受限于较矮的一边 min(8,3) = 3。面积 = 3×6 = 18。 没超过当前最大 49。
- 13右端较矮或持平(高 3 ≤ 左端 8)。移走它 → r 退到下标 6。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 14看这对线:左下标 1(高 8)、右下标 6(高 8),间距 = 6−1 = 5。能盛多少,要看较矮的那条。
- 15水位受限于较矮的一边 min(8,8) = 8。面积 = 8×5 = 40。 没超过当前最大 49。
- 16右端较矮或持平(高 8 ≤ 左端 8)。移走它 → r 退到下标 5。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 17看这对线:左下标 1(高 8)、右下标 5(高 4),间距 = 5−1 = 4。能盛多少,要看较矮的那条。
- 18水位受限于较矮的一边 min(8,4) = 4。面积 = 4×4 = 16。 没超过当前最大 49。
- 19右端较矮或持平(高 4 ≤ 左端 8)。移走它 → r 退到下标 4。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 20看这对线:左下标 1(高 8)、右下标 4(高 5),间距 = 4−1 = 3。能盛多少,要看较矮的那条。
- 21水位受限于较矮的一边 min(8,5) = 5。面积 = 5×3 = 15。 没超过当前最大 49。
- 22右端较矮或持平(高 5 ≤ 左端 8)。移走它 → r 退到下标 3。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 23看这对线:左下标 1(高 8)、右下标 3(高 2),间距 = 3−1 = 2。能盛多少,要看较矮的那条。
- 24水位受限于较矮的一边 min(8,2) = 2。面积 = 2×2 = 4。 没超过当前最大 49。
- 25右端较矮或持平(高 2 ≤ 左端 8)。移走它 → r 退到下标 2。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 26看这对线:左下标 1(高 8)、右下标 2(高 6),间距 = 2−1 = 1。能盛多少,要看较矮的那条。
- 27水位受限于较矮的一边 min(8,6) = 6。面积 = 6×1 = 6。 没超过当前最大 49。
- 28右端较矮或持平(高 6 ≤ 左端 8)。移走它 → r 退到下标 1。矮的一边是瓶颈,留着只会更差,移走它才可能换来更高的水位。
- 29两指针走到这里,最优容器锁定:左下标 1(高 8)、右下标 8(高 7),宽 7、水位 7 → 面积 49。
⚠️ 容易写错的地方
✗ 错:用较高的一边算水位
✓ 对:水位 = min(左高, 右高)
水从矮的一侧溢出,矮的才是瓶颈
✗ 错:总移动左指针
✓ 对:每轮移走「较矮」的那一端
移走高的只会让面积更小,移矮的才可能翻盘
✗ 错:两端相等时纠结
✓ 对:随便移哪端都行
移任一端宽度都缩,水位由这相等值封顶,不会漏更优解
完整代码(Python / C++ / Java)
Python
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 bestC++
int maxArea(vector<int>& h){
int l = 0, r = h.size() - 1, best = 0;
while(l < r){
int area = min(h[l], h[r]) * (r - l);
best = max(best, area);
if(h[l] < h[r]) l++;
else r--;
}
return best;
}Java
public int maxArea(int[] height) {
int l = 0, r = height.length - 1, best = 0;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
best = Math.max(best, area);
if (height[l] < height[r]) l++;
else r--;
}
return best;
}复杂度
时间
O(n)
l 和 r 合计向中间走一遍
空间
O(1)
只用 l/r/best 几个变量
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 盛最多水的容器 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
移走较矮端,会不会漏掉更优的解?+
不会。设 height[l] < height[r],固定 l 时它已和最远的 r 配对、宽度最大,而水位被 l 封顶;再往里收 r 只会让宽度更小、水位还是 ≤ height[l],面积必更小。所以以 l 为矮端的所有组合中当前这个已是最优,可放心丢掉 l、右移。
为什么暴力是 O(n²),这里能降到 O(n)?+
暴力枚举所有线对共 C(n,2) 个。双指针每轮排除掉一个「不可能再更优」的矮端,n 步走完,把平方降成线性。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 盛最多水的容器 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。