LeetCode 11 盛最多水的容器:从暴力枚举到双指针优化
LeetCode 11 盛最多水的容器:从暴力枚举到双指针优化
最近刷到 LeetCode 11《盛最多水的容器》,这道题是双指针算法的经典入门题。刚开始看到题目时,我的第一反应是暴力枚举所有可能的柱子组合,但仔细分析后会发现,这种做法效率太低,而双指针才是这道题真正的核心。
题目描述
给定一个数组 height,数组中的每个元素表示一根柱子的高度。
从中选择两根柱子,与 x 轴组成一个容器,求能够容纳的最大水量。
例如:
height = [1,8,6,2,5,4,8,3,7]
最终答案为:
49
对应的是高度为 8 和 7 的两根柱子。
容器面积怎么计算?
很多同学第一次做这道题时容易忽略一点:
容器高度并不是由高的柱子决定,而是由较矮的柱子决定。
因为水位超过矮柱子时就会流出去。
所以面积计算公式为:
面积 = 宽度 × 较矮柱子的高度
其中:
宽度 = 两根柱子的距离
高度 = min(左柱子高度, 右柱子高度)
例如:
height = [1,8,6,2,5,4,8,3,7]
选择:
8 和 7
对应下标:
1 和 8
则:
宽度 = 8 - 1 = 7
高度 = min(8,7) = 7
面积 = 7 × 7 = 49
最容易想到的方法:暴力枚举
最直接的思路是:
固定左边柱子,然后让右边柱子遍历后面的所有位置。
把所有组合都计算一遍。
例如:
for i in range(len(height)):
for j in range(i + 1, len(height)):
area = (j - i) * min(height[i], height[j])
完整代码:
class Solution:
def maxArea(self, height):
max_area = 0
for i in range(len(height)):
for j in range(i + 1, len(height)):
width = j - i
h = min(height[i], height[j])
area = width * h
if area > max_area:
max_area = area
return max_area
时间复杂度
O(n²)
因为:
- 第一层循环 n 次
- 第二层循环最多 n 次
总共需要计算:
n × n
级别的数据。
当数组很长时,LeetCode 会直接超时。
双指针优化
既然暴力法要枚举所有组合,那有没有办法减少无意义的计算?
答案就是双指针。
初始化
让两个指针分别放在数组两端:
left = 0
right = len(height) - 1
此时宽度最大。
关键问题:为什么移动矮的柱子?
这是整道题最重要的地方。
假设当前:
左高 = 3
右高 = 8
容器高度:
min(3,8)=3
面积:
宽度 × 3
如果移动右边(高柱子):
宽度变小
高度仍然最多是3
面积一定不会变大。
因为决定水位的是左边那根高度为3的柱子。
而如果移动左边(矮柱子):
宽度虽然变小
但新的柱子可能更高
例如:
3 → 7
那么容器高度就有机会提高。
面积才可能增大。
因此:
移动高柱子一定没有收益,移动矮柱子才有机会获得更大的面积。
这就是双指针成立的核心依据。
双指针代码
from typing import List
class Solution:
def maxArea(self, height: List[int]) -> int:
left = 0
right = len(height) - 1
max_area = 0
while left < right:
width = right - left
h = min(height[left], height[right])
area = width * h
if area > max_area:
max_area = area
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_area
示例运行过程
输入:
[1,8,6,2,5,4,8,3,7]
第一次
left=0 (1)
right=8 (7)
宽度=8
高度=1
面积=8
更新:
max=8
左边更矮:
left++
第二次
left=1 (8)
right=8 (7)
宽度=7
高度=7
面积=49
更新:
max=49
右边更矮:
right--
第三次
left=1 (8)
right=7 (3)
面积=18
小于49。
继续移动右指针。
后面不断缩小区间,虽然还能算出很多面积,但都无法超过49。
最终答案:
49
为什么时间复杂度是 O(n)?
因为:
while left < right:
整个过程中:
- left 只会向右移动
- right 只会向左移动
每个位置最多访问一次。
因此:
时间复杂度:O(n)
空间复杂度:O(1)
相比暴力法的 O(n²),效率提升非常明显。
这道题真正要掌握什么?
很多人记住了代码,却没有理解双指针思想。
实际上这道题最重要的收获是:
暴力法
枚举所有可能。
时间复杂度 O(n²)
双指针法
利用题目性质进行剪枝。
移动高柱子没有意义
移动矮柱子才有机会获得更优解
从而把:
O(n²)
优化到:
O(n)
这也是双指针算法最经典的应用场景之一。
总结
这道题的核心只有两句话:
- 容器面积 = 宽度 × 两侧较矮柱子的高度;
- 双指针每次只移动较矮的那一侧,因为移动较高的一侧不可能得到更大的面积。
理解了这两个结论,代码基本就能顺利写出来。
后面刷到三数之和、接雨水、有序数组求和等经典双指针题目时,也会不断用到类似的思想。
更多推荐

所有评论(0)