盛最多水的容器:双指针法的巧妙运用(leetcode 11)
问题描述
给定一个长度为 n 的整数数组 height,有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
我的思考历程
最初看到这个问题,我直觉上想到的是双指针法。设置左右两个指针,分别指向数组的开头和结尾,然后逐步向中间移动。但关键问题是:应该移动哪个指针?
第一次尝试:移动较高的指针
我最初的思路是:"让高的柱子先走,因为它可能找到更高的搭档"。于是写出了这样的代码:
cpp
// 初始错误思路 if (height[left] > height[right]) {h = min(height[left], height[--right]); } else {h = min(height[++left], height[right]); }
但测试后发现结果不对!这让我陷入了深深的思考...
突破时刻:为什么应该移动较矮的指针?
经过1个小时的思考和数学推导,我终于明白了其中的奥秘:
数学证明
设当前状态:容量 = min(h_left, h_right) × width
如果移动较高的指针:
新宽度 = width - 1
新高度 ≤ min(h_left, h_right)
∴ 新容量 ≤ min(h_left, h_right) × (width - 1) < 原容量
如果移动较矮的指针:
新宽度 = width - 1
新高度可能 > min(h_left, h_right)
∴ 新容量可能增加!
直观理解
想象两个柱子,一个矮一个高:
移动高的柱子:容器的高度上限被矮柱子限制,宽度减少,容量必然减少
移动矮的柱子:虽然宽度减少,但可能找到更高的柱子,从而增加高度补偿宽度的损失
最终解决方案
cpp
class Solution { public:int maxArea(vector<int>& height) {int maxArea = 0;int left = 0;int right = height.size() - 1;while (left < right) {int currentHeight = min(height[left], height[right]);int currentWidth = right - left;maxArea = max(maxArea, currentHeight * currentWidth);// 关键 insight:移动较矮的指针if (height[left] < height[right]) {left++;} else {right--;}}return maxArea;} };
算法分析
时间复杂度:O(n),每个元素最多被访问一次
空间复杂度:O(1),只使用常数额外空间
正确性保证:每次移动都排除了不可能产生更大容量的情况