5.盛最多水的容器

85 阅读2分钟

题目链接

给定一个长度为 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

题解1 暴力解法

思路

题目要求我们求解容器的最大值,可以把 height[i] 看作一块木板,其实求的是面积的最大值。而两块木板的面积的最大值等于木板之间的间距 j - i * min(height[i], height[j]),因为水会从矮的那边流出去,所以要取最小值。

最简单的解法就是两层循环,第一层先确定 i 第二层从 i + 1 开始确定 j,然后求他们的面积。

而我们如何保证取到的是面积最大呢?在遍历的时候比较答案缓存下来。

一定要是两块最高的木板才是最大值吗?不一定,因为面积还受到间距的影响。

代码

function maxArea(height: number[]): number {
    const len: number = height.length;
    let result: number = 0;

    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            const area = (j - i) * Math.min(height[i], height[j]);
            result = Math.max(result, area);
        }
    }

    return result;
};

时空复杂度分析

时间复杂度:两层循环铁 O(n^2)在leetcode上会超时

空间复杂度:没有额外缓存 O(1)

题解2 相向双指针

思路

我们思考一下暴力解法,内层循环会依次遍历一遍剩余数值,而如果最右侧的没有木板为 0,那么每次遍历它时都是空,即使是最高的木板和它构成容器都是徒劳,所以可以优化这里的重复计算。

所以可以用两个指针来相向遍历(两个指针从数组的两端开始,向中间移动) height 数组,比较两个木板的高度,每次移动较矮的指针。

代码

function maxArea(height: number[]): number {
    const len: number = height.length;
    let result: number = 0;

    for (let i = 0, j = len - 1; i < j;) {
        const area: number = (j - i) * Math.min(height[i], height[j]);
        result = Math.max(result, area);
        if (height[i] > height[j]) {
            j--;
        } else {
            i++;
        }
    }

    return result;
};

时空复杂度分析

时间复杂度:一次遍历 O(n)

空间复杂度:没有额外存储,O(1)