🌊 LeetCode 11:盛最多水的容器 —— 双指针法如何从 O(n²) 优化到 O(n)

60 阅读3分钟

你是否曾被一道看似简单却难以下手的题目困扰?比如:

给定一个非负整数数组 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))

🎯 核心思想:只移动较矮的一侧

假设我们从左右两端开始,维护两个指针 lr

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]
步骤lrhwarea操作
108min(1,7)=188移动 l(1<7)
218min(8,7)=7749移动 r(8>7)
.....................

最终最大面积:49


⏱️ 复杂度分析

项目暴力解法双指针法
时间复杂度O(n²) ❌O(n) ✅
空间复杂度O(1)O(1)
是否推荐

✅ 总结

这道题的关键在于理解:

面积由宽度和较矮的一边共同决定,而宽度只能越来越小,所以必须优先提升较矮的一边。

双指针法正是基于这个贪心策略,将时间复杂度从 O(n²) 降到 O(n),是该问题的标准最优解。