题目描述

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

返回 滑动窗口中的最大值

思路

考虑利用一个单调的双端队列(deque)实现对区间最大值的维护。这个deque的前端就是这个区间的最大值,而其后续的部分则是接下来的窗口中有可能取到的最大值。

再考虑一个双指针,中间的部分即为窗口。我们只需要考虑left和right的元素即可。

例如:

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

1
2
3
4
5
6
7
8
滑动窗口的位置                最大值             deque
--------------- ----- --------
[1 3 -1] -3 5 3 6 7 3 3, -1
1 [3 -1 -3] 5 3 6 7 3 3, -1, -3
1 3 [-1 -3 5] 3 6 7 5 5
1 3 -1 [-3 5 3] 6 7 5 5, 3
1 3 -1 -3 [5 3 6] 7 6 6
1 3 -1 -3 5 [3 6 7] 7 7

对于最开始的3个,最大值是3,然而在窗口向右移动时,若3弹出,-1有可能成为某个区间的最大值,因此在deque中保留了-1.

当5进入,它比deque中任何一个元素都大(只要它比deque front大),所以deque全部pop,并加入5.

如果进入的某个元素不大于deque front,却大于deque back,那么deque中比这个元素小的数在今后的区间内就不会成为最大值了,因此将其全部pop。

然而在实现的过程中,最令我困惑的是,窗口左端何时出,右端何时入。

声明需要的变量。

1
2
3
4
5
deque<int> q;
vector<int> res;
int left = 0;
int right = 0;
int len = nums.size();

首先解决前k个的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while(right < k){
if(nums[right] > q.front()){
while(!q.empty()){
q.pop_back();
}
q.push_front(nums[right]);
}else{
while(!q.empty() && nums[right] > q.back()){
q.pop_back();
}
q.push_back(nums[right]);
}
right++;
}
res.push_back(q.front());

再看其余的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while(right < len){
if(nums[left] == q.front()){
q.pop_front();
}
if(nums[right] > q.front()){
while(!q.empty()){
q.pop_front();
}
q.push_front(nums[right]);
}else
if(nums[right] <= q.front()){
while(!q.empty() && q.back() < nums[right]){
q.pop_back();
}
q.push_back(nums[right]);
}
right++;
left++;
res.push_back(q.front());
}

事实上,我们需要使此后的区间长度为k+1,因为需要判断front是否需要弹出。

完整代码

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q;
vector<int> res;
int left = 0;
int right = 0;
int len = nums.size();
while(right < k){
if(nums[right] > q.front()){
while(!q.empty()){
q.pop_back();
}
q.push_front(nums[right]);
}else{
while(!q.empty() && nums[right] > q.back()){
q.pop_back();
}
q.push_back(nums[right]);
}
right++;
}
res.push_back(q.front());
while(right < len){
if(nums[left] == q.front()){
q.pop_front();
}
if(nums[right] > q.front()){
while(!q.empty()){
q.pop_front();
}
q.push_front(nums[right]);
}else
if(nums[right] <= q.front()){
while(!q.empty() && q.back() < nums[right]){
q.pop_back();
}
q.push_back(nums[right]);
}
right++;
left++;
res.push_back(q.front());
}
return res;
}
};