给定一个长度为 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));
综上所述,双指针法及其优化版本是解决该问题的高效方法,时间复杂度较低。