【LeetCode】11. 盛最多水的容器
解题思路
盛最多水容器问题的核心在于双指针法的应用。该方法的思路如下:
- 双指针初始化:左右指针分别指向数组两端,初始宽度最大。
- 贪心策略:每次移动较矮的指针(木桶效应),因为移动较高的指针无法增加容量。
- 动态更新:计算当前指针范围内的水量,并维护全局最大值。
关键步骤
- 初始化指针:
left=0,right=len(height)-1。 - 循环收缩:计算当前水量
min(height[left], height[right]) * (right-left)。 - 指针移动:较矮的一侧向内移动,直到指针相遇。
Golang代码实现
func maxArea(height []int) int { left, right := 0, len(height)-1 maxWater := 0 for left < right { // 计算当前水量(木桶效应,取较矮的柱子) currentHeight := min(height[left], height[right]) currentWidth := right - left currentWater := currentHeight * currentWidth // 更新最大水量 if currentWater > maxWater { maxWater = currentWater } // 移动较矮的指针(贪心策略) if height[left] < height[right] { left++ } else { right-- } } return maxWater } // 辅助函数:取较小值 func min(a, b int) int { if a < b { return a } return b }
复杂度分析
| 指标 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 单次遍历数组,指针移动总次数为 n-1 |
| 空间复杂度 | O(1) | 仅需常数级变量 |
运行示例验证
func main() { // 示例1 height1 := []int{1, 8, 6, 2, 5, 4, 8, 3, 7} fmt.Println(maxArea(height1)) // 输出: 49 // 示例2 height2 := []int{1, 1} fmt.Println(maxArea(height2)) // 输出: 1 }
核心逻辑说明
-
双指针策略:
- 初始时,左右指针分别指向数组两端,宽度最大(
width = len(height)-1)。 - 每次计算水量后,移动较矮的指针(如左指针指向的柱子高度为1,右指针指向的柱子高度为7,则移动左指针)。
- 通过贪心策略,逐步缩小搜索范围,确保不漏掉可能的更大容量。
- 初始时,左右指针分别指向数组两端,宽度最大(
-
水量计算原理:
水量公式为min(height[left], height[right]) * (right - left),其中:- 较矮柱子决定高度:容器的高度受限于较矮的柱子(木桶效应)。
- 宽度逐步减少:指针移动时宽度减少,但可能通过找到更高的柱子弥补损失。
-
边界处理:
- 当数组长度为2时直接计算(如示例2)。
- 无需处理空数组或单元素数组(题目保证
n >= 2)。
算法正确性证明
假设最大水量对应的柱子为 i 和 j(i < j)。双指针法会通过以下步骤覆盖所有可能的候选解:
- 初始状态:左指针在0,右指针在
n-1。 - 指针移动:若左指针先到达
i,则右指针会逐步左移至j;若右指针先到达j,左指针会逐步右移至i。 - 水量更新:每次移动较矮指针时,确保不会错过更高柱子的可能性。
扩展讨论
-
暴力法的局限性:
暴力法需要遍历所有组合(时间复杂度 O(n²)),在n=1e5时会超时,而双指针法的时间复杂度为 O(n),适用于大规模数据。 -
数学规律优化:
可通过提前终止循环优化性能(如当前最大水量已超过剩余宽度的潜在可能),但实现复杂且收益有限。
通过双指针法,该实现以最优时间和空间复杂度解决了问题,完美覆盖所有测试用例。

浙公网安备 33010602011771号