盛最多水的容器
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
速度
题目描述
给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1
输入:height = [1,8,6,2,5,4,8,3,7]
输出:49
垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49(索引 1 和 8 之间,min(8,7)×7=49)。
示例 2
输入:height = [1,1]
输出:1
两条线高度均为 1,宽度为 1,面积为 1。
模拟答题者思考
1. 最直接:枚举所有线对 (i, j),面积 min(height[i], height[j]) × (j-i),双重循环 O(n²),n=10⁵ 会超时。
2. 重复在哪里?固定 l 时从右往左扫 r,和固定 r 从左往右扫 l 本质一样——都在暴力枚举宽度。
3. 双指针:从两端出发,宽度最大;要尝试更大面积只能缩宽度,所以每次必须移动一侧指针。
4. 贪心关键:移动较短的那一侧。较短边是当前容器的「短板」,留着它面积不可能变大(宽度还变小了);移走短板才有机会遇到更高的线。
5. 正确性直觉:若移走较长边,宽度 -1 且高度仍受短板限制,面积一定不比现在大,可以安全丢弃这一侧的所有配对。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
l, r | int | 定义:左右两条候选垂线的下标,当前考虑的容器边界 维护:初始 l=0, r=n-1,每次向内移动较短一侧的指针更新:当 height[l] <= height[r] 时 l++,否则 r-- |
ans | int | 定义:遍历过程中见过的最大容器面积 维护:每轮用当前 (l, r) 计算面积并与 ans 取 max更新: ans = max(ans, min(height[l], height[r]) * (r - l)) |
落码步骤
1. 初始化 l=0, r=n-1, ans=0
2. 当 l < r:计算 area = min(height[l], height[r]) * (r - l),更新 ans
3. 若 height[l] <= height[r],l++;否则 r--
4. 循环结束返回 ans
代码实现
class Solution:
def maxArea(self, height: List[int]) -> int:
l, r = 0, len(height) - 1
ans = 0
while l < r:
# 当前容器面积:短板高度 × 宽度
h = min(height[l], height[r])
ans = max(ans, h * (r - l))
# 移动较短一侧,才可能找到更大面积
if height[l] <= height[r]:
l += 1
else:
r -= 1
return ans
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int h = min(height[l], height[r]);
ans = max(ans, h * (r - l));
if (height[l] <= height[r])
l++;
else
r--;
}
return ans;
}
};
// 时间 O(n),空间 O(1)
复杂度分析
时间复杂度
O(n)
空间复杂度
O(1)
常见坑
面积公式是 min(左高, 右高) × 宽度,不是 max 或两高之和。
移动指针时应移较短一侧(相等时移哪边都行,习惯 l++);移较长一侧会漏掉更优解。
循环条件是 l < r 而非 l <= r,至少两条线才能构成容器。
必测边界 Case
Case 1:最短数组
height = [1, 1] → 1
Case 2:单调递增
height = [1, 2, 3, 4, 5] → 6(首尾 min(1,5)×4=4,但中间 2 和 5 可得 6)
Case 3:含零高度
height = [0, 2, 0] → 0(与 0 高度线构成的容器面积为 0)