11.滑动窗口最大值

99 阅读2分钟

题目链接

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

返回 滑动窗口中的最大值 。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7]

示例 2:

输入:nums = [1], k = 1 输出:[1]

题解1 暴力解法

思路

寻求滑动窗口的最大值,可以把滑动窗口看作一个子数组,每次对子数组求得最大值即可。

暴力解法会超时

代码

function maxSlidingWindow(nums: number[], k: number): number[] {
    let left: number = 0;
    let right: number = k - 1;
    const result: number[] = [];

    while (right < nums.length) {
        result.push(Math.max(...nums.slice(left++, ++right)));
    }

    return result;
};

时空复杂度分析

时间复杂度:O(n * k) ,其中 k 最大为 num.length,所以可以看成是 O(n^2)

空间复杂度:O(k) 每次调用 slice 创建一个长度为 k 的数组

题解2 单调队列

思路

上述暴力的重复计算就在于寻找左指针和右指针之间子数组的最大值,设想一个极端场景,如果整个数组都是一样的数,那么是不是每次都要重新寻找最大的值。

针对于这个重复计算,可以利用单调队列来优化,就是在遍历数组的过程中维护一个队列来记录当前滑动窗口当中最大的值的下标。如果队尾比当前遍历的数字小,那就删除队尾。如果当前下标减去队头下标比窗口 k 还大,那就删除队头。

代码

function maxSlidingWindow(nums: number[], k: number): number[] {
    const result: number[] = [];
    const stk: number[] = [];
    
    for (let i = 0; i < nums.length; i++) {
        while (stk.length && nums[stk[stk.length - 1]] <= nums[i]) {
            stk.pop();
        }

        stk.push(i);
        if (i - stk[0] >= k) {
            stk.shift();
        }
        if (i >= k - 1) {
            result.push(nums[stk[0]]);
        }
    }

    return result;
};

时空复杂度分析

时间复杂度:一次遍历 O(n),每个下标最多入队一次,出队一次

空间复杂度:O(k)stk 数组里最多长度为 k