刷题日记 - 盛最多水的容器

107 阅读2分钟

题目

image.png

思路

看到题目的第一眼,就觉得,这是个动态规划题,公式也很明显:

设开始下标为s,结束下标为e,会有以下概念:
设C(s, e)为s到e的面积,其值为(e - s) * Math.min(height[e], height[s])
则M(s, e) = Math.max(M(s + 1, e), M(s, e - 1), C(s, e))

代码写起来也比较简单

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let record = new Map();

    function getVal(s, e) {
        if (s >= e) return 0;

        let key = `${s},${e}`;
        let val = 0;
        if (record.has(key)) {
            val = record.get(key);
        } else {
            val = (e - s) * Math.min(height[s], height[e]);
            val = Math.max(getVal(s + 1, e), getVal(s, e - 1), val);
            record.set(key, val);
        }
        return val;
    }

    return getVal(0, height.length - 1);
};

但很遗憾,提交没能通过,错误原因如下:

image.png 原来是超内存了,想了一下,毕竟是O(n2)的复杂度,中间生成的数据,也会是n2的量,还是挺大的,所以挂掉还是挺正常的。
仔细思考一下,发现题目中好像有些条件还没有利用上,即存在短板效应,如果两个数a和b,取的是较小的那个值进行计算面积,另外的一个值再大也没用。
来一步一步分析:

  • 按照动态规划,我们需要比较M(s + 1, e), M(s, e - 1), C(s, e)三者的大小
  • 其中M(s + 1, e), M(s, e - 1)会出现相当多重复的计算,我们中间是用record存储了,没什么问题,
  • M(s + 1, e)相比M(s, e - 1)会多出以下计算:C(s + 1, e), C(s + 2, e),C(s + 3, e)...C(e - 1, e)
  • 但如果height[s] > height[e],这些计算好像都没有必要去做,因为短板效应,会导致C(s, e)肯定是大于等于这些值的。那么在这种情况下,M(s + 1, e)是没必要计算的【重复的计算M(s, e - 1)中有了,多余的计算又是没必要的】
  • 同理如果height[s] <= height[e],也会有一些计算,是没必要的

修改一下代码:

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let record = new Map();

    function getVal(s, e) {
        if (s >= e) return 0;

        let key = `${s},${e}`;
        let val = 0;
        if (record.has(key)) {
            val = record.get(key);
        } else {
            val = (e - s) * Math.min(height[s], height[e]);
            // 选择性的进行计算
            if (height[s] > height[e]) {
                val = Math.max(getVal(s, e - 1), val);
            } else{
                val = Math.max(getVal(s + 1, e), val);
            }
            record.set(key, val);
        }
        return val;
    }
    
    return getVal(0, height.length - 1);
};

再去提交一下,就过了:

image.png

虽然过了,但这击败5%,实在不太好看,再整理一下,上面的代码,其实不就是个双指针吗,也没必要存储中间的数据,再改一下:

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let max = 0;
    let s = 0, e = height.length - 1;
    while (s < e) {
        max = Math.max(max, (e - s) * Math.min(height[s], height[e]));
        if (height[s] < height[e]) {
            s++;
        } else {
            e--;
        }
    }

    return max;
};

结果:

image.png

yysy,js的执行时间不太稳定,同样的代码每次排名都不一样,好不容易才跑到一个比较好看的数据😄