Container With Most Water(盛最多水的容器)
题目 / Problem
中文: 给定一个长度为 n 的整数数组 height。第 i 条垂直线的两个端点是 (i, 0) 和 (i, height[i])。
找出两条垂直线,使它们与 x 轴构成的容器能够容纳最多的水,返回最大容量。容器不能倾斜。
English: Given an integer array height of length n, the ith vertical line has endpoints (i, 0) and (i, height[i]).
Find two lines that, together with the x-axis, form a container holding the maximum amount of water. Return that maximum amount. The container may not be slanted.
示例 / Examples
Example 1
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
选择下标 1 和 8 的两条线:
Choose the lines at indices 1 and 8:
宽度 / Width = 8 - 1 = 7
高度 / Height = min(8, 7) = 7
面积 / Area = 7 × 7 = 49
Example 2
Input: height = [1,1]
Output: 1
宽度 / Width = 1
高度 / Height = 1
面积 / Area = 1
约束 / Constraints
n === height.length2 <= n <= 10^50 <= height[i] <= 10^4
容量计算 / Area Formula
选择下标 left 和 right 的两条线时:
For lines at indices left and right:
width = right - left
水面高度由较短的那条线决定,因为水会从较短一侧溢出:
The water height is limited by the shorter line because water spills over that side:
containerHeight = min(height[left], height[right])
因此容器面积为:
Therefore, the container area is:
area = (right - left) × min(height[left], height[right])
解题思路:双指针 / Approach: Two Pointers
初始化两个指针:
Initialize two pointers:
left = 0
right = height.length - 1
此时两条线之间的宽度最大。每一轮:
The two lines initially have the maximum possible width. In each iteration:
- 计算当前容器面积并更新最大值。
Calculate the current area and update the maximum. - 比较左右两条线的高度。
Compare the two line heights. - 移动较短一侧的指针,尝试找到更高的线。
Move the pointer at the shorter line, hoping to find a taller line. - 当
left === right时停止。
Stop whenleft === right.
为什么移动较短的一侧? / Why Move the Shorter Side?
假设:
Suppose:
height[left] < height[right]
当前面积由 height[left] 限制。如果保留左侧较短的线并移动右指针:
The current area is limited by height[left]. If the shorter left line is kept while moving the right pointer:
- 宽度一定减小。
The width definitely decreases. - 新的容器高度最多仍然是
height[left]。
The new container height is still at mostheight[left].
因此面积不可能变大。
Therefore, the area cannot increase.
只有移动较短的左侧指针,才有机会遇到一条更高的线,从而弥补宽度减小带来的损失。
Only moving the shorter left pointer may discover a taller line that can compensate for the reduced width.
右侧较短时同理,移动 right。
The argument is symmetric when the right side is shorter: move right.
相等高度如何处理? / What If the Heights Are Equal?
如果 height[left] === height[right],移动任意一侧都可以。当前实现移动右指针:
If height[left] === height[right], either pointer may be moved. This implementation moves the right pointer:
if (height[left] < height[right]) {
left++;
} else {
right--;
}
保留其中一条相同高度的线,并不会错过更优答案。
Keeping either one of two equal-height lines does not miss a better answer.
JavaScript 实现 / JavaScript Implementation
/**
* @param {number[]} height
* @return {number}
*/
function maxArea(height) {
let left = 0;
let right = height.length - 1;
let maximumArea = 0;
while (left < right) {
const width = right - left;
const containerHeight = Math.min(height[left], height[right]);
const currentArea = width * containerHeight;
maximumArea = Math.max(maximumArea, currentArea);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maximumArea;
}
执行过程 / Walkthrough
以 height = [1,8,6,2,5,4,8,3,7] 为例:
For height = [1,8,6,2,5,4,8,3,7]:
left | right | 左高 / Left height | 右高 / Right height | 面积 / Area | 移动 / Move |
|---|---|---|---|---|---|
| 0 | 8 | 1 | 7 | 8 × 1 = 8 | left++ |
| 1 | 8 | 8 | 7 | 7 × 7 = 49 | right-- |
| 1 | 7 | 8 | 3 | 6 × 3 = 18 | right-- |
| 1 | 6 | 8 | 8 | 5 × 8 = 40 | right-- |
| 1 | 5 | 8 | 4 | 4 × 4 = 16 | right-- |
| 1 | 4 | 8 | 5 | 3 × 5 = 15 | right-- |
| 1 | 3 | 8 | 2 | 2 × 2 = 4 | right-- |
| 1 | 2 | 8 | 6 | 1 × 6 = 6 | right-- |
遍历过程中的最大面积是 49。
The maximum area encountered is 49.
为什么双指针不会漏掉答案? / Why Does Two Pointers Not Miss the Answer?
每一步都会安全排除一条不可能参与更优解的边界线:
Each step safely eliminates one boundary line that cannot participate in a better solution:
- 如果左线较短,那么所有以该左线为边界、右端点位于当前
right左侧的容器,宽度更小且高度不会超过左线,因此面积不可能超过当前面积。
If the left line is shorter, every container using that left line with a right endpoint inside the current range has smaller width and height no greater than the left line, so none can exceed the current area. - 因此可以安全丢弃当前左线并执行
left++。
The current left line can therefore be discarded safely withleft++. - 右线较短时,同理可以安全执行
right--。
The same reasoning safely discards the right line when it is shorter.
算法不会枚举所有线对,但每次跳过的组合都已经被证明不可能更优。
The algorithm does not enumerate every pair, but every skipped pair has been proven incapable of improving the answer.
与暴力解法对比 / Comparison with Brute Force
暴力解法枚举所有两条线的组合:
Brute force examines every pair of lines:
组合数量 / Number of pairs = n × (n - 1) / 2
时间复杂度为 O(n²),当 n 达到 10^5 时无法接受。
Its time complexity is O(n²), which is too slow when n reaches 10^5.
双指针每轮至少移动一个指针,总共最多移动 n - 1 次。
The two-pointer approach moves at least one pointer per iteration, for at most n - 1 moves total.
复杂度 / Complexity
- 时间复杂度 / Time:
O(n) - 空间复杂度 / Space:
O(1)
易错点 / Common Pitfalls
- 容器高度由两条线中较短的一条决定,应使用
Math.min()。
The shorter line determines the water height, so useMath.min(). - 宽度是下标之差
right - left,不是元素数量。
Width is the index differenceright - left, not the number of array elements. - 每轮应移动较短的一侧,而不是较高的一侧。
Move the shorter side, not the taller side. - 必须在移动指针之前计算当前面积。
Calculate the current area before moving a pointer. - 不能把数组柱高相加;水的面积是宽度乘以受限高度。
Do not add line heights; water area is width multiplied by the limiting height. - 题目不允许倾斜容器,因此两侧高度不同时仍以较短侧为水面高度。
The container cannot be slanted, so unequal sides still use the shorter height as the water level.