11. 盛最多水的容器(Container With Most Water)

难度:中等 | 主题:对撞双指针

题目

给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

示例

输入:[1,8,6,2,5,4,8,3,7]

输出:49

解释:垂直线 i=1(高度8) 和 j=8(高度7) 之间距离 7,min(8,7)×7=49


思路

先讲个故事:选木桶装水

你面前有一排高矮不一的木桶,你只能选两个桶,用它们之间的矩形区域装水。

装水量取决于两件事:两个桶中较矮的那个的高度 × 两个桶的距离

你站在最左和最右两个桶前,犯了难:“我要往中间挪一挪吗?挪哪个?”

如果移动较的那个桶——宽度变小了,但高度不会超过较矮的那个(被矮桶限制),面积只减不增,白费力气。

如果移动较的那个桶——宽度虽然变小了,但万一遇到更高的桶,高度可能变大,面积有可能变大。

这个直觉就是双指针解法的核心:每次淘汰不可能成为最优解的边界


引导式推导:从暴力到对撞双指针

暴力法 O(n²)

两两枚举所有柱子对,算面积取最大值。n=10⁵ 时没法用。

寻找规律

面积公式:area = min(height[l], height[r]) * (r - l)

假设 height[l] < height[r](左边矮),面积 = height[l] × (r-l)。

如果移动右指针(高的那边),新面积 = min(height[l], height[r-1]) × (r-1-l)。

由于 min 最多不超过 height[l](矮的没变),而宽度变小了 → 新面积 ≤ 旧面积。

结论:移动较矮的一边才可能让面积变大

反证法证明正确性

假设最优解是柱子对 (i, j)。当左指针 < i 或右指针 > j 时,总有一个指针会先到达最优边界。因为每次都移动较矮的指针,较矮的那侧会被”淘汰”——如果这个位置不可能是最优解的一部分,移走它是安全的。


graph LR

    subgraph 指针移动过程

        A["l=0, r=n-1"] --> B{"height[l] < height[r]?"}

        B -->|是| C["l++"]

        B -->|否| D["r--"]

        C --> E["更新 max_area"]

        D --> E

        E --> F{l < r?}

        F -->|是| B

        F -->|否| G["返回 max_area"]

    end


代码

 
def maxArea(self, height):
 
    l, r = 0, len(height) - 1
 
    ans = 0
 
    while l < r:
 
        area = min(height[l], height[r]) * (r - l)
 
        ans = max(ans, area)
 
        if height[l] <= height[r]:  # 左边更矮 → 移动左指针,赌右边有更高的
 
            l += 1
 
        else:                       # 右边更矮 → 移动右指针
 
            r -= 1
 
    return ans
 

复杂度

指标解释
时间O(n)每个元素被访问一次
空间O(1)两个指针
暴力枚举O(n²)两两枚举所有柱子对

实战考量

频率分析

出现在:字节/美团/阿里 高频,双指针的经典”试金石”。关键在于能不能证出’为什么移动矮的而不是高的’

延伸思考

Q:为什么移动高的不会得到更大面积?

A:面积受限于矮的。移动高的那边,宽度缩小,高度 ≤ 原来的矮的高度,面积只减不增。

Q:证明这个算法不会错过最优解?

A:每次淘汰一个”不可能作为边界”的位置——如果当前位置是较矮的,它不可能和任何更靠内的柱子组成更优解(因为宽度变小,高度被它限制)。问题规模每次减一,保证最终收敛到最优解。

Q:和 42 接雨水的区别?

A:11 求最大矩形面积(选两个柱子围水),42 求累计凹槽水量(所有凹坑积水和)。但都用对撞双指针 + 移动矮的。详见 42接雨水

Q:如果柱子不是等距的呢?

A:排序后贪心或动态规划,但已经不是这道题的范围了。

易错点

  • 面积公式是 min(h[l], h[r]) * (r-l),不是 (r-l+1)

  • <= 还是 < 移动指针都可以,但要一致

  • r - l 在移动指针前计算(移动后宽度变了)


生活类比

最大容器 → 对撞双指针 → 淘汰不可能

像一群朋友在一条河边选位置扎营。

最左和最右的人先商量:“咱们之间能搭多大的帐篷?”

左边的人比较矮,看不到河对岸——右边的人说:“你往前走走吧,你那边可能有更好的视野。”

矮的那个往前走了,留下高的那个在原地等待——因为高的那个看到的风景不会比矮的好多少。

每次淘汰一个”不可能成为最优”的位置,问题规模就小一点。


相关题目

题目关系
42接雨水姐妹题,同为对撞双指针 + 移动矮的,11 求最大面积,42 求累计水量
167两数之和II-输入有序数组同族双指针,有序数组左右夹逼
11盛最多水的容器本题,对撞双指针入门

→ 返回题单:LeetCode学习路线图 > 一、数组与哈希(含双指针、矩阵)

速记卡(面试闪卡)

Q1:一句话讲清「11. 盛最多水的容器(Container With Most Water)」到底是什么?

A:用对撞双指针在数组里选两条线,使围成的矩形面积最大,每次淘汰矮边。

Q2:一、题目与面积本质 —— 怎么理解?

A:像一排高矮木桶,选两个用中间矩形装水,水量=矮桶高×间距。本质是求 min(高)×宽 的最大矩形(Container Area)。

Q3:二、对撞双指针思路 —— 怎么理解?

A:像两人从河两岸往中间走,每次让矮的那个挪一步,因为移高的只会更矮。每次淘汰不可能最优的边界,O(n) 收拢(Two Pointers)。

Q4:三、代码实现要点 —— 怎么理解?

A:像左右手夹住水桶,循环里算 min(h[l],h[r])×(r-l) 并更新最大值,谁矮谁动。用 <= 移动指针,宽度在动之前算(Pointer Update)。

Q5:四、复杂度与易错点 —— 怎么理解?

A:像一次散步遍历完数组,时间 O(n) 空间 O(1)。易错在面积用 min 而非求和、r-l 要在移动前算(Time/Space Complexity)。

Q6:核心速记主线有哪些?

  • 面积=min(两线高)×间距,受矮边限制

  • 对撞双指针:每次移动较矮的一边

  • 时间 O(n) 空间 O(1),优于暴力 O(n²)

  • 易错:用 min 不是求和,宽度移动前算

口诀

A:盛水看矮边,

移矮不移动高;

双指针收拢,

最大面积找。

相关链接