题目:

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

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

示例 1:

1
2
3
输入: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:

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

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

解题思路:

这题其实很容易用错数据结构,很多人一开始肯定会想 “既然是维护滑动窗口内的最大值,那么我直接使用大根堆不就行了么?” 其实使用大根堆是错的,因为大根堆需要排序,时间复杂度会高,其实我们还有一种更快更容易写的数据结构 => 单调队列

单调队列 + 滑动窗口

单调队列 顾名思义,单调,说明队列内的元素要么是单调递增,要么是单调递减。

[0, 6, 8, 10] => 这是单调队列

那么理解了这个含义,其实这道题就很简单了,只要在滑动窗口滑动的时候不断维护我们的单调队列,那么我们这道题就接出来了。

开始滑滑动窗口:

窗口移除的元素与单调队列队首相等时弹出队首

窗口添加的元素与单调队列的队尾进行比较,若队尾 <= 添加的元素 则弹出队尾的元素,直到队尾 > 添加的元素然后将元素推进队尾。

代码:

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
from collections import deque
# 要保证 q[-1] 一直是最大
q = deque()
for i in range(0, k):
while q and nums[i] > q[0]:
q.popleft()
q.appendleft(nums[i])

ans = [q[-1]]
for i in range(1, len(nums) - k + 1):
l, r = nums[i - 1], nums[i + k - 1]
if q and l == q[-1]:
q.pop()
while q and r > q[0]:
q.popleft()
q.appendleft(r)
ans.append(q[-1])
return ans

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}

vector<int> ans = {nums[q.front()]};
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};