前言
「盛最多水的容器」是力扣上一道经典的双指针题目。给定一个整数数组,数组中的每个值代表一根垂直于 x 轴的柱子高度,我们需要从中选出两根柱子,使它们与 x 轴围成的容器能够容纳最多的水。这个问题看似需要枚举所有柱子组合,但借助双指针的单调性,可以在一次遍历内高效求解。
双指针解法的核心思想是:从数组两端各放一个指针,每次比较两根柱子的高度,移动较矮一侧的指针向内收缩。因为容器的高度由较短的柱子决定,移动较矮一侧才有可能让高度变大,从而获得更大的容积。通过不断更新最大体积,最终得到答案。
题目描述:
题目来源:力扣「盛最多水的容器」
给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。
找出其中的两条线,使它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
横坐标表示数组的下标 i,纵坐标表示数组的值即 nums[i]。
算法原理:
利用单调性,采用双指针的算法解决此问题。
值得注意的是"盛水的体积大小取决于最短的那块木板"。
笔记:盛最多水的容器(双指针解法)
题目核心
容器盛水量公式:V = h × w
- w:左右两个柱子之间的宽度(下标之差)
- h:容器高度,由左右两根柱子中较短的那一根决定
示例数组:[1, 8, 6, 2, 5, 4, 8, 3, 7]
双指针思路
- 初始化:左指针
left放在数组最左端,右指针right放在数组最右端。 - 循环终止条件:
left与right相遇时停止。 - 指针移动规则:比较左右两个柱子高度,移动高度更小的那一侧指针向内收缩。
分步图解示例
以示例数组 [1, 8, 6, 2, 5, 4, 8, 3, 7] 为例,逐步演示双指针的移动过程。下表记录了每一步中左右指针的位置、对应柱子高度、容器宽度、当前体积以及历史最大体积。
| 步骤 | left | right | heightleft | heightright | 宽度 w | 体积 V | 最大体积 Vmax | 移动指针 |
|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 8 | 1 | 7 | 8 | 8 | 8 | left++ |
| 2 | 1 | 8 | 8 | 7 | 7 | 49 | 49 | right-- |
| 3 | 1 | 7 | 8 | 3 | 6 | 18 | 49 | right-- |
| 4 | 1 | 6 | 8 | 8 | 5 | 40 | 49 | left++ |
| 5 | 2 | 6 | 6 | 8 | 4 | 24 | 49 | left++ |
| 6 | 3 | 6 | 2 | 8 | 3 | 6 | 49 | left++ |
| 7 | 4 | 6 | 5 | 8 | 2 | 10 | 49 | left++ |
| 8 | 5 | 6 | 4 | 8 | 1 | 4 | 49 | left++ |
从表格中可以看到,当 left 指向下标 1(高度 8)、right 指向下标 8(高度 7)时,容器体积达到最大值 49,这也是本题的最终答案。整个过程中,左右指针从两端向中间靠拢,每个元素最多被访问一次,因此时间复杂度为 O(n)。
移动原理(单调性)
向内收缩时,宽度 w 一定会变小,分两种情况:
- 若移动较短柱子,新柱子高度 > 当前短柱高度:高度 h 不变,但宽度 w 减小,总体积 V 变小;
- 若移动较短柱子,新柱子高度 < 当前短柱高度:高度 h 减小,宽度 w 也减小,总体积 V 变小。
长柱子一侧不用移动:因为当前短柱已经限制了容器高度,向内移动长柱子只会让宽度变小,不可能得到更大的容积。所以直接舍弃当前较短的柱子,向内移动指针。
- 每次移动指针后,重新计算当前容器体积,持续更新记录最大体积
Vmax。
cpp
class Solution
{
public:
int maxArea(vector<int>& height)
{
int left = 0;
int right = height.size()-1;
//记录体积的最大值
int ret = 0;
while(left < right)
{
//宽度为左减去右
int v = min(height[left],height[right])*(right -left);
//计算完体积 更新一下最大的体积
ret =max(v,ret);
//移动指针
if(height[left]<height[right])
{
left++;
}
else
{
right--;
}
}
return ret;
}
};
复杂度分析
双指针解法的时间复杂度为 O(n),空间复杂度为 O(1)。
时间复杂度为 O(n) 的原因:左右指针分别从数组两端出发,每次循环只移动其中一个指针,且两个指针相遇时循环结束。在整个过程中,每个元素最多只会被访问一次,因此总的操作次数与数组长度 n 成正比。
空间复杂度为 O(1) 的原因:算法只使用了 left、right、ret 等常数个额外变量,没有借助额外的数据结构,因此空间占用与输入规模无关。
总结
双指针解法是解决「盛最多水的容器」问题的高效方法,其关键在于利用容器的短板效应:每次移动较矮一侧的指针,在宽度减小的同时有机会提升容器高度,从而在 O(n) 时间内找到最大容积。
该解法适用于需要在一维数组中寻找满足某种约束的最优区间问题,尤其是当问题具有单调性、且最优解可以通过收缩边界逐步逼近时。理解这一思路,有助于举一反三地解决其他双指针类题目。