LeetCode 11:盛最多水的容器(双指针问题) —— 题解
👋 欢迎阅读
一.题目
11. 盛最多水的容器 - 力扣(LeetCode)
🎯 欢迎来到「盛最多水的容器」题解之旅!本文将带你从“寻找两条线与 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,即能容纳的最大水量。
三.代码
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:若允许倾斜,盛水量将不再由最短边决定,而是由倾斜后液面与容器壁的接触位置决定,问题复杂度剧增,需引入流体力学模型或几何计算,不再适合用双指针简单求解;题目限定“不能倾斜”正是为了保持问题的几何简洁性。
祝你在算法之路上越走越稳,早日攻克每一道难题!下次见 🚀✨
