33-盛最多水的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
方法一:暴力枚举法
该方法通过遍历数组中所有可能的两条线的组合,计算它们所构成容器的水量,然后找出其中的最大值。
function maxArea(height: number[]): number {
let maxWater = 0;
const n = height.length;
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
const width = j - i;
const minHeight = Math.min(height[i], height[j]);
const water = width * minHeight;
maxWater = Math.max(maxWater, water);
}
}
return maxWater;
}
复杂度分析
- 时间复杂度:(O(n^2)),因为需要使用两层嵌套循环来遍历所有可能的线对,n 是数组的长度。
- 空间复杂度:(O(1)),只使用了常数级的额外空间来存储最大水量。
方法二:双指针法
使用两个指针分别指向数组的首尾,计算当前指针所指两条线构成的容器水量,然后根据两条线的高度,移动较短的线对应的指针,继续计算水量,直到两个指针相遇。
function maxArea(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const water = width * minHeight;
maxWater = Math.max(maxWater, water);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
}
复杂度分析
- 时间复杂度:(O(n)),只需要遍历数组一次,n 是数组的长度。
- 空间复杂度:(O(1)),只使用了常数级的额外空间来存储指针和最大水量。
方法三:优化双指针法(提前终止部分计算)
在双指针法的基础上,当移动指针时,如果新的指针所指的线高度小于等于之前的线高度,那么可以直接跳过,因为这样构成的容器水量不会更大。
function maxArea(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const water = width * minHeight;
maxWater = Math.max(maxWater, water);
if (height[left] < height[right]) {
const currentLeftHeight = height[left];
while (left < right && height[left] <= currentLeftHeight) {
left++;
}
} else {
const currentRightHeight = height[right];
while (left < right && height[right] <= currentRightHeight) {
right--;
}
}
}
return maxWater;
}
复杂度分析
- 时间复杂度:\(O(n)\),虽然增加了一些内部的指针移动判断,但总体上仍然只需要遍历数组一次,n 是数组的长度。
- 空间复杂度:\(O(1)\),只使用了常数级的额外空间来存储指针和最大水量。
你可以使用以下方式测试这些函数:
const height = [1, 8, 6, 2, 5, 4, 8, 3, 7];
console.log(maxArea(height));
综上所述,双指针法及其优化版本是解决该问题的高效方法,时间复杂度较低。