挑战LeetCode热题100(TS)打卡第八天

97 阅读3分钟

挑战LeetCode热题100(TS)

打卡第八天

滑动窗口

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

返回 滑动窗口中的最大值

1.滑动窗口最大值

示例 1:

输入: nums = [1,3,-1,-3,5,3,6,7], k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7      5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

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

思路一:暴力破解

function maxSlidingWindow(nums: number[], k: number): number[] {
    // 循环
    // const n = nums.length
    // const res = []
    // for(let i=0;i<=n-k;i++) {
    //     let j = i
    //     let max = nums[j]
    //     while(j<i+k) {
    //         max = Math.max(nums[j], max)
    //         j++
    //     }
    //     res.push(max)
    // }
    // return res
    // 
};
  • 时间复杂度:O(n*n)

思路二:双端队列;

function maxSlidingWindow(nums: number[], k: number): number[] {
    const result: number[] = [];
    const deque: number[] = [];

    for (let i = 0; i < nums.length; i++) {
        // 如果双端队列中的元素超出了窗口范围,将其移出队列
        if (deque.length > 0 && deque[0] < i - k + 1) {
            deque.shift();
        }

        // 移除队列中比当前元素小的元素,因为它们不可能成为最大值
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }

        // 将当前元素的索引加入队列
        deque.push(i);

        // 如果窗口的起始索引已经在合法范围内,将队列头部元素作为最大值添加到结果中
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }

    return result;
}

思路三:优先队列

  • 初始时,我们将数组 nums 的前 kkk 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
  • 我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),,表示元素 num 在数组中的下标为 index。
class Solution {
  maxSlidingWindow(nums: number[], k: number): number[] {
    const n = nums.length;
    const maxHeap: [number, number][] = [];

    for (let i = 0; i < k; i++) {
      maxHeap.push([-nums[i], i]);
    }
    maxHeap.sort((a, b) => a[0] - b[0]);

    const ans: number[] = [-maxHeap[0][0]];

    for (let i = k; i < n; i++) {
      maxHeap.push([-nums[i], i]);
      while (maxHeap[0][1] <= i - k) {
        maxHeap.shift();
      }
      ans.push(-maxHeap[0][0]);
    }

    return ans;
  }
}

const solution = new Solution();
const nums = [1, 3, -1, -3, 5, 3, 6, 7];
const k = 3;
const result = solution.maxSlidingWindow(nums, k);
console.log(result); // 输出 [3, 3, 5, 5, 6, 7]

总结

如果使用优先队列方式构建大顶堆,采用暴力破解很耗时,虽然内存开销较小。但优先队列不太容易理解,还需要先掌握树,队列,堆,栈等相关概念后,再基于ts去实现后。再做此题很容易想到有限队列