算法 - 滑动窗口最大值
1. 题目
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。你只可以看到在滑动窗口内的 k 个数字,滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值组成的数组。
示例:
1 | 输入: nums = [1,3,-1,-3,5,3,6,7], k = 3 |
2. 解题思路
2.1 暴力法
每个窗口遍历求最大,O(n·k),k 大时超时,仅作对比。
2.2 单调递减双端队列(最优)
维护一个存下标的双端队列,保证队列对应元素值从队头到队尾单调递减,队头始终是当前窗口最大值的下标。
对每个新元素 i:
- 淘汰过期:若队头下标已滑出窗口(
queue[0] <= i - k),从队头弹出。 - 维护单调性:从队尾开始,凡是值
<= nums[i]的下标全部弹出——它们被nums[i]「又新又大」地压制,永远不可能再成为答案。 - 把
i入队尾。 - 当
i >= k - 1开始收集答案,队头即当前窗口最大值。
- 时间复杂度:O(n),每个元素最多入队出队各一次;空间复杂度:O(k)。
3. TypeScript 实现
1 | function maxSlidingWindow(nums: number[], k: number): number[] { |
说明:JS 数组的
shift()是 O(n),严格最坏复杂度会退化;面试可指出用「头指针 index」模拟队列头、或双向链表实现真正 O(1) 出队。此处为清晰用shift。
4. 面试延伸
- 单调栈 vs 单调队列:单调栈解决「下一个更大元素」等;单调队列额外处理「窗口左边界过期」,本题是其模板题。
- 变形:窗口最小值(改成单调递增队列)、绝对值不超过 limit 的最长子数组等,都基于单调队列。
- 追问:为什么队列存下标而不是值?因为需要靠下标判断队头是否滑出窗口。
- 另一路线:分块 / 稀疏表(ST) 预处理区间最大值,也能做到接近 O(n),可作为思路拓展。
难度:困难 | LeetCode 239 | 单调队列模板