先问一个问题:你在刷 LeetCode Hot 100 的时候,是不是经常遇到双指针的题,看答案觉得“就这?”,但自己拿到新题还是一脸懵?如果你正好刷到第 11 题“盛最多水的容器”,并且对“为什么移动矮的那一边”这个操作背后的逻辑有点似懂非懂,那这篇文章就是给你写的。
这道题在 Hot 100 里属于经典中的经典,难度标成 medium,但说实话代码量连十行都不到。它难的不是写代码,而是理解那个双指针的移动策略为什么是对的。很多人靠背题解记住了“谁矮谁移动”,但面试官一追问原理就露馅。这篇文章我会从最朴素的暴力解法开始,一步步推出双指针解法,再用严格的思路证明它为什么不会漏掉最优解,最后结合我自己刷题踩过的坑,帮你把这道题的每个细节都吃透。不管你是刚入门的新手,还是准备面试想系统过一遍 Hot 100 的选手,这篇都能帮你在双指针这个专题上少走弯路。
1. 题目理解与暴力解法推演
1.1 题目到底在问什么
题目描述本身不难懂:给定一个长度为 n 的整数数组 height,数组里的每个数代表一根垂直线的高度,你要在其中选两根线,和 x 轴一起构成一个容器,问这个容器最多能装多少水。
举个例子,height = [1, 8, 6, 2, 5, 4, 8, 3, 7],你选下标 2(高度 6)和下标 8(高度 7)这两根线,容器宽度是 8 - 2 = 6,容器高度取决于较矮的那根线(因为水会从矮的那边溢出),也就是 min(6, 7) = 6,所以面积是 6 × 6 = 36。当然这个不是最优解,最优解是选下标 1(高度 8)和下标 8(高度 7),宽度 7,高度 min(8, 7) = 7,面积 49。
这里有几个关键点要提醒一下:第一,容器的容量取决于短板,不是长板,这就是经典的“木桶效应”;第二,题目说的是垂直线,所以容器壁是垂直于 x 轴的,不需要考虑倾斜的情况;第三,数组元素的数量上限是 10 的 5 次方,这个数量级直接决定了暴力解法不可行,后面会仔细算这笔账。
1.2 最直观的暴力枚举法
拿到这道题,正常人第一反应都是两层循环枚举所有两根线的组合。用代码写出来也很简单:
java复制public int maxArea(int[] height) {
int n = height.length;
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int area = (j - i) * Math.min(height[i], height[j]);
ans = Math.max(ans, area);
}
}
return ans;
}
这段代码的逻辑没有任何问题,穷举了所有可能的两根线组合,计算每种情况的面积,然后取最大值。但问题在于时间复杂度是 O(n²)。当 n = 10^5 时,需要比较的组合数是 n(n-1)/2,大约是 5 × 10^9 次,也就是五十亿次计算。在 LeetCode 的评测环境下,这个数量级的操作通常需要几十秒甚至更久,必然超时。
所以,这道题真正的挑战不是“怎么算面积”,而是“怎么不用全部枚举就能找到最大面积”。这就要用到双指针了,但双指针不是凭空蹦出来的,它背后有一套非常漂亮的排除逻辑。我们先把暴力解法和双指针解法的差异放在一起看:
| 解法 | 时间复杂度 | 空间复杂度 | 能通过 LeetCode |
|---|---|---|---|
| 暴力枚举 | O(n²) | O(1) | 不能,超时 |
| 双指针 | O(n) | O(1) | 能,稳过 |
从 O(n²) 到 O(n),这中间不是靠什么玄学优化,而是靠“排除不可能的解”来减少计算量。下面我们重点拆解这个优化是怎么一步步想出来的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从暴力到双指针:思考过程的完整还原
2.1 面积公式里藏着什么样的优化空间
我们先回到面积公式:S = (j - i) × min(height[i], height[j])。这个公式由两部分组成:宽度 (j - i),以及高度 min(height[i], height[j])。
暴力解法是把所有 i 和 j 的组合都算一遍。但如果你仔细观察,会发
