“看似暴力,实则优雅”——用两个指针,装下整个海洋。
大家好,我是你们的算法搭子 👋
今天我们要一起攻克 LeetCode 上一道经典中等题:第11题「盛最多水的容器」(Container With Most Water)。
别被“中等”吓到!这道题不仅思路巧妙,而且是双指针算法的教科书级案例。掌握它,你就能在面试中自信地说:“双指针?我熟!”
🧩 题目描述(LeetCode #11)
给你一个长度为 n 的整数数组 height,其中每个元素代表一条垂直线的高度。
现在请你找出两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
注意:你不能倾斜容器,且
n至少为 2。
示例:
输入:height = [1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水的最大值为 49。
❌ 暴力解法:O(n²) 的陷阱
最直观的想法是什么?
枚举所有可能的左右边界,计算面积,取最大值。
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
area = (j - i) * Math.min(height[i], height[j]);
max = Math.max(max, area);
}
}
时间复杂度:O(n²)
空间复杂度:O(1)
但问题是——当 n = 10⁵ 时,循环次数高达 50亿次!直接超时 💥
所以,我们必须更聪明一点。
✨ 灵光一现:双指针的优雅策略
🤔 核心思想
- 初始时,左指针在最左(0),右指针在最右(n-1) ,此时宽度最大。
- 容器的面积 = 宽度 × min(左高, 右高)
- 要想面积更大,必须尝试更高的边,因为宽度只会越来越小!
🔑 关键洞察:
移动较矮的那一边,才有可能找到更大的面积!
为什么?
假设 height[left] < height[right],那么当前面积受限于 left 的高度。
即使你移动 right,宽度变小,高度也不会超过 height[left],面积只会更小。
所以,唯一有希望提升面积的方式,是移动 left,寻找更高的左边界!
同理,如果右边更矮,就移动右边。
这就是贪心 + 双指针的完美结合!
💡 正确代码(JavaScript)
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let ans = 0;
let left = 0;
let right = height.length - 1;
while(left < right) {
let area = (right - left)*(Math.min(height[left],height[right]));
ans = Math.max(area,ans);
if(height[left]>height[right]){
right--;
}else{
left++;
}
}
return ans;
};
✅ 时间复杂度:O(n)
✅ 空间复杂度:O(1)
🎯 为什么这个策略不会漏掉最优解?
这是很多人卡住的地方: “万一最优解在中间,而我跳过了怎么办?”
我们来反证一下:
假设真实最优解是 (i, j),而我们的双指针在某一步跳过了 i 或 j。
但注意:只有当一边比另一边矮时,我们才会移动它。
在到达 (i, j) 之前,左右指针一定分别从两端向内收缩。
只要 i 和 j 还没相遇,它们就一定会被遍历到 —— 因为我们只在“不可能更优”的情况下才移动指针。
换句话说:每一步都保留了“可能成为最优解”的那一侧。
这不是玄学,是数学上的贪心正确性保证!
🧠 延伸思考
- 如果允许倾斜容器?(那就不是这道题了 😂)
- 如果数组中有负数?(题目规定高度 ≥ 0)
- 能不能用单调栈?(可以,但没必要,双指针更优)
✅ 总结:双指针的三大特征
当你遇到以下情况,可以考虑双指针:
- 数组或字符串问题
- 需要比较两端元素(如回文、对撞)
- 存在“移动某一边能排除无效解”的贪心策略
而这道题,完美契合!
🌟 最后送你一句口诀:
“水桶短板定容量,双指贪心移短边;宽减高升寻最大,一次遍历稳如山。”
如果你觉得这篇文章帮你打通了双指针的任督二脉,欢迎点赞 ❤️、收藏 ⭐、转发 🔄!
也欢迎在评论区留下你的解法或疑问,我们一起刷穿 LeetCode!