239. 滑动窗口最大值 🟥
题目
leetcode 困难
题目
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 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]
题解
单调递减队列
- 时间复杂度:O(N)
for 里面嵌套了 while 就觉得是 O(N2),这是错误的!
请注意,每个元素最多只会入队一次,出队一次。总操作次数是 2N,所以时间复杂度是绝对的 O(N)。 - 空间复杂度:O(K)
js
var maxSlidingWindow = function (nums, k) {
const result = []
// 双端队列,存储的是元素的【索引】!(用来判断是否过期)
const deque = []
for (let i = 0; i < nums.length; i++) {
console.log('♻️ i', i, '【', nums[i], '】')
console.log('deque', deque.map(index => nums[index]).join(', '))
// 1. 清理过期元素
// 队列头部的索引如果已经滑出了当前窗口 [i - k + 1, i],就把它踢掉
if (deque.length > 0 && deque[0] <= i - k) {
deque.shift()
}
// 2. 保持单调递减
// 如果新来的 nums[i] 大于等于队列尾部的元素,尾部元素直接被淘汰出局
while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) {
deque.pop()
}
// 3. 入列
deque.push(i)
console.log('deque', deque.map(index => nums[index]).join(', '))
// 4. 记录当前窗口的最大值
// 只有当窗口完全形成时(i 走到 k-1 及以后),才开始收集结果
if (i >= k - 1) {
result.push(nums[deque[0]]) // 队头永远是当前窗口的老大
}
}
return result
}
console.log('🌰', maxSlidingWindow([1, 3, -1, -3, 5, 3, 6, 7], 3))
console.log('-------')
console.log('🌰', maxSlidingWindow([1, 3, -1, -3, 5, 3, 2, 2], 3))
执行结果
♻️ i 0 【 1 】
deque
deque 1
♻️ i 1 【 3 】
deque 1
deque 3
♻️ i 2 【 -1 】
deque 3
deque 3, -1
♻️ i 3 【 -3 】
deque 3, -1
deque 3, -1, -3
♻️ i 4 【 5 】
deque 3, -1, -3
deque 5
♻️ i 5 【 3 】
deque 5
deque 5, 3
♻️ i 6 【 6 】
deque 5, 3
deque 6
♻️ i 7 【 7 】
deque 6
deque 7
🌰 [ 3, 3, 5, 5, 6, 7 ]
-------
♻️ i 0 【 1 】
deque
deque 1
♻️ i 1 【 3 】
deque 1
deque 3
♻️ i 2 【 -1 】
deque 3
deque 3, -1
♻️ i 3 【 -3 】
deque 3, -1
deque 3, -1, -3
♻️ i 4 【 5 】
deque 3, -1, -3
deque 5
♻️ i 5 【 3 】
deque 5
deque 5, 3
♻️ i 6 【 2 】
deque 5, 3
deque 5, 3, 2
♻️ i 7 【 2 】
deque 5, 3, 2
deque 3, 2
🌰 [ 3, 3, 5, 5, 5, 3 ]