你是否曾被一道看似简单却难以下手的题目困扰?比如:
给定一个非负整数数组
height,表示柱子的高度,选择任意两个柱子作为容器的两边,求能容纳的最大水量。
这道题就是经典的“盛最多水的容器”。今天我们就来一步步拆解它,并告诉你为什么双指针法是最佳解法。
🧠 问题理解
我们有若干个柱子,高度不同。你可以选任意两个柱子组成一个“容器”,其容量由:
- 两柱之间的距离(宽度)
- 两柱中较矮的那个高度(决定水位)
即:
面积 = (j - i) * min(height[i], height[j])
目标:找到最大面积。
❌ 暴力解法(O(n²))—— 会超时!
最直观的想法是枚举所有可能的组合:
var maxArea = function(height) {
let res = 0;
for (let i = 0; i < height.length; i++) {
for (let j = i + 1; j < height.length; j++) {
res = Math.max(res, (j - i) * Math.min(height[i], height[j]));
}
}
return res;
};
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
✅ 能通过小数据测试
❌ 大数据会超时(如 n=10⁴)
✅ 优化方案:双指针法(O(n))
🎯 核心思想:只移动较矮的一侧
假设我们从左右两端开始,维护两个指针 l 和 r:
let l = 0;
let r = height.length - 1;
每次计算当前面积:
h = min(height[l], height[r])
w = r - l
area = h * w
然后判断哪边更矮:
- 如果左边矮 → 移动
l++ - 如果右边矮 → 移动
r--
💡 为什么这样是对的?
因为面积由 宽度 × 较矮的一边 决定。
- 宽度在不断减小(指针向中间靠拢)
- 所以只有当较矮的一边变高时,才有可能获得更大面积
- 移动较高的那根柱子,不会增加面积(因为水位受限于较矮者)
✅ 你的代码解析(已优化)
var maxArea = function(height) {
let res = 0;
let n = height.length;
let l = 0;
let r = n - 1;
while (r > l) {
let h = Math.min(height[l], height[r]); // 当前水位
let w = r - l; // 宽度
res = Math.max(res, h * w); // 更新最大面积
// 移动较矮的一侧
if (height[l] > height[r]) {
r--;
} else {
l++;
}
}
return res;
};
🔍 逐行解释
| 代码 | 说明 |
|---|---|
let l = 0; let r = n - 1; | 从左右两端开始 |
while (r > l) | 只要还有空间就继续 |
h = min(height[l], height[r]) | 水位由较矮柱子决定 |
w = r - l | 当前宽度 |
res = max(res, h * w) | 更新最大面积 |
if (height[l] > height[r]) { r--; } else { l++; } | 移动较矮的一侧 |
🧪 示例演示
height = [1,8,6,2,5,4,8,3,7]
| 步骤 | l | r | h | w | area | 操作 |
|---|---|---|---|---|---|---|
| 1 | 0 | 8 | min(1,7)=1 | 8 | 8 | 移动 l(1<7) |
| 2 | 1 | 8 | min(8,7)=7 | 7 | 49 | 移动 r(8>7) |
| ... | ... | ... | ... | ... | ... | ... |
最终最大面积:49
⏱️ 复杂度分析
| 项目 | 暴力解法 | 双指针法 |
|---|---|---|
| 时间复杂度 | O(n²) ❌ | O(n) ✅ |
| 空间复杂度 | O(1) | O(1) |
| 是否推荐 | 否 | 是 |
✅ 总结
这道题的关键在于理解:
面积由宽度和较矮的一边共同决定,而宽度只能越来越小,所以必须优先提升较矮的一边。
双指针法正是基于这个贪心策略,将时间复杂度从 O(n²) 降到 O(n),是该问题的标准最优解。