👋 欢迎阅读

一.题目
🎯 欢迎来到「盛最多水的容器」题解之旅! 本文将带你从"寻找两条线与 x 轴构成的最大容器"这一几何直觉出发,深入理解双指针(对撞指针) 的经典应用,并掌握如何通过每次移动较短的边来高效逼近最优解。
在开始之前,建议你先:
-
了解题目背景 :这是 LeetCode 11 题,给定数组
height,每个元素表示一根垂直线的高度,选择两根线与 x 轴构成容器,求能容纳的最大水量 (面积 = 宽度 × 高度,高度取两根线中较短者)。本质上,我们需要在 O(n)O(n) 时间内找到最大矩形面积,暴力枚举不可行,双指针是本题的核心解法。 -
明确学习目标 :掌握双指针收缩策略 ------左指针指向数组左端,右指针指向右端,计算当前面积;然后移动高度较小的一端 (因为面积受限于较短边,移动较高边不会让面积增大,只有移动较短边才有可能遇到更高的边)。理解为什么这种"每次舍弃较短的边"的贪心策略能保证不漏掉最优解,并熟练实现循环与面积更新的代码逻辑。
-
准备好环境 :建议在本地 IDE 或 LeetCode 在线编辑器中打开代码,边看边运行,亲手验证示例(如
height = [1,8,6,2,5,4,8,3,7]输出49)。
本文将从问题转化、双指针策略设计、正确性直觉 到代码实现,层层递进。即使你对双指针还不熟悉,我们也会从"容器高度取决于短板,想变大就要换掉短板 "这一直观出发,让你轻松抓住核心思想 ------每次向内移动较短的线,宽度虽减,但有机会遇到更高的线从而提升容量,而移动较长的线则不可能让容量变大。现在,让我们一起用两根指针扫描数组,找出那个能盛最多水的容器吧! 📏💧
二.做题思路
一、问题分析(前置分析)
给定一个整数数组 height,其中每个元素表示一条垂直线的高度,下标代表 x 轴坐标。选择两条线,与 x 轴构成容器,求 能容纳的最大水量 。
容量 = 宽度(两下标之差)× 高度(两条线中较矮的高度) 。
核心观察 :容器的容量由 较短的边 决定。要最大化面积,需要综合考虑宽度和高度。
二、算法策略(双指针)
-
使用 左右双指针
left和right分别指向数组的两端。 -
循环条件 :
left < right。 -
每次计算当前面积:
area = min(height[left], height[right]) * (right - left),并更新最大面积maxVal。 -
移动指针规则 :移动高度较小的那个指针 (即若
height[left] < height[right],则left++;否则right--)。 -
直到两指针相遇,结束循环。
三、正确性说明(简单版本)
容器的高度由 较短的边 决定。若当前左指针高度小于右指针,则 移动右指针 (较高的边)时,宽度减小,而高度不会超过当前左指针的高度,因此面积只会减小或不变,不可能找到更大的面积。因此,移动较短的边 是唯一可能使面积增大的选择。这样逐步缩小搜索范围,不会遗漏任何可能的最大值,从而保证最终结果正确。
四、实现细节(边界防护)
-
初始化
left = 0,right = n-1,maxVal = 0。 -
计算宽度时使用
right - left。 -
高度取
min(height[left], height[right])。 -
更新最大值后,根据比较结果移动指针。
-
循环直到
left == right。 -
时间复杂度 O(n),空间复杂度 O(1)。
五、返回值(目标映射)
返回 maxVal,即 能容纳的最大水量。
三.代码
cpp
class Solution
{
public:
int maxArea(vector<int>& height)
{
// 算法思路:双指针法
// 左指针指向数组左端,右指针指向数组右端。
// 计算当前左右指针所构成的容器面积 = min(height[left], height[right]) * (right - left)。
// 然后移动高度较小的指针(因为容器面积受限于较短边,移动较高边不会使面积增大,只有移动较短边才可能找到更大的面积)。
// 直到左右指针相遇,过程中记录最大面积。
int n = height.size();
int left = 0; // 左指针,初始指向最左边
int right = n - 1; // 右指针,初始指向最右边
int maxVal = 0; // 记录当前找到的最大矩形面积(避免与 std::max 冲突,用 maxVal)
// 当左指针小于右指针时,持续计算面积
while (left < right)
{
// 取当前左右指针中较小的那个高度(因为容器的宽度取决于较短边)
int rectangle_wide = 0;
if (height[left] < height[right])
{
rectangle_wide = height[left];
}
else
{
rectangle_wide = height[right];
}
// 容器的长度(底边宽度)为右指针与左指针的差值
int rectangle_long = right - left;
int new_max = rectangle_long * rectangle_wide;
// 更新最大面积
if (new_max > maxVal)
{
maxVal = new_max;
}
// 移动指针的策略:移动高度较小的那一端,以期望找到更高的边,从而可能增大面积
if (height[left] < height[right])
{
left++; // 左边较低,左指针向右移动
}
else
{
right--; // 右边较低(或相等,移动任意一边),右指针向左移动
}
}
// 返回最大面积
return maxVal;
}
};
四、流程图

🎯 闭幕

🎉 恭喜你完成了「盛最多水的容器」问题的学习!
为了巩固知识并进一步拓展,建议你:
🚀 动手实践
在 LeetCode 上提交代码,尝试不同的测试用例。
💡 深入思考
-
本题使用 双指针 从数组两端向中间收缩。为什么移动指针时,要选择 移动高度较小的那一边 ,而不是高度较大的那一边?请从 面积计算公式 的角度解释原理。
-
如果左右指针高度 相等 时,代码中选择了移动右指针(
right--),那么移动左指针是否也可以?这两种选择对最终结果有影响吗? -
双指针法的 时间复杂度为 O(n) ,而暴力枚举所有组合为 O(n²)。请说明为什么移动指针的过程中 不会遗漏 可能的最优解?
📚 延伸挑战
-
如果题目改为 找出面积最大的三个柱子构成的容器(即选择三条线,取最左和最右为边界,中间柱子不影响面积),算法该如何改造?
-
如果允许 倾斜容器(即容器可以倾斜,盛水量不再由最短边决定),问题会变得怎样?为什么题目要特别说明"不能倾斜容器"?
如果你觉得本文对你有所帮助,欢迎:
👍 点赞 / 收藏
👤 关注作者 ,获取更多题解
💬 留言交流你的疑问或优化思路
📌 深入思考答案
-
移动较矮边 是因为容器的盛水量由 较短边 决定(短板效应),若移动较高边,宽度减小而高度不变或更低,面积只会 减少或不变 ;只有移动较矮边,才有机会遇到更高的边,从而 增大面积。
-
高度相等时,移动左指针或右指针 均可 ,因为无论移动哪一边,宽度都在减小,而高度受限于相等的值,后续面积不会超过当前值,因此选择任意一边对最终结果 没有影响。
-
双指针 不会遗漏最优解 ,因为每一步移动较矮边时,相当于 排除了该边与其他所有边组合的可能性,而这些被排除的组合的面积都不会超过当前面积(由于宽度更大但高度受限于该矮边),因此安全剪枝。
🔍 延伸挑战答案
-
挑战1 :若选择三条线,实际盛水仍由 最左和最右 两条边界决定,中间线不参与盛水,因此问题退化为原问题,只需找到最优的两条边界,中间选任意一条不影响结果,算法无需改动。
-
挑战2 :若允许倾斜,盛水量将不再由最短边决定,而是由倾斜后液面与容器壁的接触位置决定,问题复杂度剧增,需引入 流体力学模型 或 几何计算 ,不再适合用双指针简单求解;题目限定"不能倾斜"正是为了保持问题的 几何简洁性。
祝你在 算法之路 上越走越稳,早日攻克每一道难题!下次见 🚀✨