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

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
核心题解:盛最多水的容器(对撞双指针法)
1. 算法核心思想:木桶效应与贪心策略
这道题计算水量的核心公式是: 容器水量 = 两个柱子之间的距离 × 两个柱子中较短的那根高度
如果我们使用暴力枚举,把所有柱子的组合都算一遍,时间复杂度是 O(n2),面对海量数据一定会超时。为了追求极致的性能,我们需要使用左右对撞双指针,并结合贪心思想来剪枝(砍掉不必要的计算)。
2. 核心灵魂拷问:为什么遇到短板才移动?
这是面试官必问的逻辑点:为什么只移动较短的那根柱子? 我们可以这样推导证明: 初始时,左右指针分别指向数组的最左侧和最右侧,此时宽度最大。接下来我们要向内收缩指针:
- 如果移动长板(较高的柱子):宽度必然减小,而受限于“木桶效应”,水面的高度最高也只能和原来的短板一样高,甚至可能变得更矮。因此,移动长板,面积一定会变小,这种操作毫无意义。
- 如果移动短板(较矮的柱子):虽然宽度减小了,但我们有可能会遇到一根比原来更高的柱子,从而打破短板限制,让水面高度大幅提升,面积才有可能变大。
因此,我们的贪心策略极其明确:每次都舍弃掉当前较短的那根柱子,向内寻找更高的可能,并在中途不断记录出现过的最大面积。
class Solution {
public:
int maxArea(vector<int>& height) {
int front = 0; // 左指针
int rear = height.size() - 1; // 右指针
int maxArea = 0; // 记录历史最高水量
while (front < rear) {
int len = rear - front; // 计算当前底边宽度
// 谁短就计算谁的面积,并把短板向内移动一步
if (height[front] < height[rear]) {
maxArea = max(maxArea, len * height[front]);
front++;
}
else {
maxArea = max(maxArea, len * height[rear]);
rear--;
}
}
return maxArea;
}
};
4. 复杂度剖析
- 时间复杂度:O(n)。左右两个指针
front和rear相对而行,直到相遇。整个数组中的每个元素最多只被访问一次,执行效率极高。 - 空间复杂度:O(1)。仅仅使用了几个额外的整型变量(指针和最大面积),没有申请任何新的数组或哈希表,内存消耗达到了理论最低。
