LeetCode-11-盛最多水的容器
in LeetCode with 0 comment

LeetCode-11-盛最多水的容器

in LeetCode with 0 comment

原题地址:盛最多水的容器

给定 n 个非负整数 $a_1$,$a_2$,...,$a_n$,每个数代表坐标中的一个点 (i, $a_i$) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, $a_i$) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明: 你不能倾斜容器,且 n 的值至少为 2。

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

示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49

基础解法

题中两根线构成的容器面积实际上就是两根线中较短的一根与要线之间的距离的乘积。所以我们只需要找出所有可能的面积值,然后求出其中的最大值即可:

/**
 * @param {number[]} height
 * @return {number}
 */
const maxArea2 = function(height) {
    let max = 0;
    for (let i = 0; i < height.length - 1; i ++) {
        // 这里进行了一定的优化,因为容器的高度为两线之间较短的一根,
        // 所以面积要大于max,两线之间的距离要大于max/height[i]
        for (let j = i + Math.ceil(max / height[i]); j < height.length; j ++) {
            let area = Math.min(height[i], height[j]) * (j - i);
            max = Math.max(max, area);
        }
    }
    return max;
};

测试:

let start = new Date();
const test = maxArea2;
console.log(test([1,8,6,2,5,4,8,3,7])); // 49
console.log(new Date().getTime() - start.getTime()); // 3

时间复杂度: 双重遍历,时间复杂度为O($n^2$)
空间复杂度: 只需要常数个额外空间,空间复杂度为O(1)

双指针法

实际上我们并不需要找出所有可能的面积,因为如果我们开始将容器的宽设为最大,接下来依次缩小宽度,要想容器的面积扩大,则容器的高必须得扩大才行。所以我们可以设两个指针分别指向数组的两端,然后根据两个高度值决定哪个指针向中间移动:

/**
 * @param {number[]} height
 * @return {number}
 */
const maxArea3 = function(height) {
    let max = 0;
    // 两个指针分别指向数组的头和尾
    let [i, j] = [0, height.length - 1];
    // 指针不能交叉
    while (i < j) {
        if (height[i] < height[j]) {
            // 如果不移动i而是移动j,则高度不会大于height[i],而宽度又减小了1,
            // 容器面积必然会缩小,所以必须移动i
            max = Math.max(height[i] * (j - i), max);
            i ++;
        } else {
            max = Math.max(height[j] * (j - i), max);
            j --
        }
    }
    return max;
};

测试:

let start = new Date();
const test = maxArea3;
console.log(test([1,8,6,2,5,4,8,3,7])); // 49
console.log(new Date().getTime() - start.getTime()); // 3

时间复杂度: 只需要一次遍历,时间复杂度为O(n)
空间复杂度: 只需要常数个额外空间,空间复杂度为O(1)