法算day5

盛最多水的容器

问题描述

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
✅ 说明:你不能倾斜容器。

示例1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:
输入:height = [1,1]
输出:1

提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104

问题分析

这个问题是要找到两条垂直线,使得它们与x轴构成的容器能容纳最多的水。容器的容量由两个因素决定:
宽度:两条线之间的水平距离
高度:两条线中较短的那条线的高度
所以容量公式为:容量 = 距离 × min(左线高度, 右线高度)

为什么移动较短的线?

这是算法的关键点:
容器的容量由较短边的高度决定
每次移动指针,宽度都会减小
如果移动较长的边,容量只会更小(因为高度不会增加,宽度减小)
如果移动较短的边,有可能找到更高的边,从而增加容量

java版(双指针法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ContainerWithMostWater {
public static void main(String[] args) {
// 测试用例1
int[] height1 = {1,8,6,2,5,4,8,3,7};
System.out.println("测试1结果: " + maxArea(height1)); // 应该输出49

// 测试用例2
int[] height2 = {1,1};
System.out.println("测试2结果: " + maxArea(height2)); // 应该输出1
}

public static int maxArea(int[] height) {
// 初始化左右指针,分别指向数组的首尾
int left = 0;
int right = height.length - 1;
// 记录最大面积
int maxArea = 0;

// 当左右指针没有相遇时,继续循环
while (left < right) {
// 计算当前两条线之间的宽度
int width = right - left;
// 容器的高度取决于较短的那条线
int currentHeight = Math.min(height[left], height[right]);
// 计算当前面积
int currentArea = width * currentHeight;
// 更新最大面积
maxArea = Math.max(maxArea, currentArea);

// 关键步骤:移动较短的那条线对应的指针
// 因为移动较长的线不会增加容量,而移动较短的线有可能找到更高的线
if (height[left] < height[right]) {
left++; // 左指针右移
} else {
right--; // 右指针左移
}
}

return maxArea;
}
}

✅ 说明:

  1. 时间复杂度:O(n) - 我们只需要遍历数组一次
  2. 空间复杂度:O(1) - 只使用了常数级别的额外空间

python版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def maxArea(height):
"""
计算容器能容纳的最大水量
使用双指针法,从数组的两端向中间移动
"""
# 初始化左指针和右指针
left = 0
right = len(height) - 1
# 初始化最大面积
max_area = 0

# 当左指针小于右指针时循环
while left < right:
# 计算当前左右指针构成的容器的面积
# 面积 = 宽度 * 高度,高度取左右指针中较小的那个
current_area = (right - left) * min(height[left], height[right])
# 更新最大面积
max_area = max(max_area, current_area)

# 移动高度较小的指针,因为移动高度较大的指针不会增加面积
if height[left] < height[right]:
left += 1 # 左指针右移
else:
right -= 1 # 右指针左移

return max_area

# 测试代码
if __name__ == "__main__":
# 示例1
height1 = [1,8,6,2,5,4,8,3,7]
print(maxArea(height1)) # 输出: 49

# 示例2
height2 = [1,1]
print(maxArea(height2)) # 输出: 1