原文

给你一个整数数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值

说一下我的解法

首先第一个窗口的最大值和最大值的下标i,一个循环即可找出 窗口前移一个位置时,我们可以假设第一个窗口的最大值还在窗口范围内,那么只需要比较一下最大值和新进入窗口的这个值,会有两种情况

  1. 最大值比新值大,维持最大值和它的下标不变
  2. 新值比最大值大,那么更新最大值和坐标

为什么需要维护坐标,因为随着窗口移动,之前的最大值可能被移出窗口,移出窗口的时候,我们要重新查找一下窗口内的最大值和其坐标

这个解法的好处是,大部分时候都不用重新查找,只需要一次简单的比较,即可知道下个窗口内的最大值;而且这个解法不受窗口大小影响,暴力解法会超时,是因为如果窗口大小是nums.length的一半的时候,复杂度会变成O(n^2/4),我这个解法,窗口大,那么重复查找窗口最大值的次数就会少,窗口小,查找窗口最大值的代价就小

具体做法是,用nums.length - k + 1长度的数组,记录每个窗口的最大值和坐标 然后循环每个窗口,按照上面的方法去比较大小。

在数值比较随机的时候,整体的复杂度略大于n,大概需要给n乘上一个系数,但是这个系数会很小

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    // let total = 0;
    const slides = nums.length - k + 1;
    const s = [];

    let maxNumber = Number.NEGATIVE_INFINITY;
    let maxIndex = -1;
    for (let i = 0; i < k; i++) {
        if (nums[i] >= maxNumber) {
            maxNumber = nums[i];
            maxIndex = i;
        }
        // total++;
    }
    s[0] = [maxNumber, maxIndex];
    // console.log(s);
    for (let i = 1; i < slides; i++) {
        let [max, index] = s[i - 1];
        if (index < i) {
            // 最大值不在窗口内了,重新计算num, index
            max = Number.NEGATIVE_INFINITY;
            index = -1;
            for (let j = i; j < k + i; j++) {
                if (nums[j] >= max) {
                    max = nums[j];
                    index = j;
                }
                // total++;
            }
            s[i] = [max, index];
        } else {
            // 只需要比较上个窗口最大值和下一个数字
            if (nums[k + i - 1] >= max) {
                max = nums[k + i - 1];
                index = k + i - 1;
            }
            s[i] = [max, index];
            // total++;
        }
    }
    // console.log(s, total);
    return s.map(el => el[0]);
};

上一篇 下一篇