题目
给你一个整数数组 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
单调队列解法
单调队列
void push_front(n)//在队头插入n
void push_back(n)//在队尾插入n
void pop_front()//删除队首元素
void pop_back()//删除队尾元素
int front()
int back()
我声明了一个变量 deque<int>window
,用于存储下标。这个变量有以下 特点:
- 变量的最前端(也就是 window.front())是此次遍历的最大值的下标
- 当我们遇到新的数时,将新的数和双项队列的末尾(也就是window.back())比较,如果末尾比新数小,则把末尾扔掉,直到该队列的末尾比新数大或者队列为空的时候才停止,做法有点像使用栈进行括号匹配。
- 双项队列中的所有值都要在窗口范围内
特点一只是方便我们获取每次窗口滑动一格之后的最大值,我们可以直接通过 window.front() 获得
通过特点二,可以保证队列里的元素是从头到尾降序的,由于队列里只有窗口内的数,所以他们其实就是窗口内第一大,第二大,第三大… 的数。
特点三就是根据题意设置的。但我们实际上只用比较现在的下标和 window.front() 就可以了,想想为什么?
Answer: 因为只要窗口内第一大元素也就是这个 window.front() 在窗口内,那我们可以不用管第二大第三大元素在不在区间内了。因为答案一定是这个第一大元素。如果 window.front() 不在窗口内,则将其弹出,第二个大元素变成第一大元素,第三大元素变成第二大元素以此类推。
代码编写的过程还要时刻检查队列是否为空防止抛出异常。
时间复杂度
代码
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
deque<int>q;
for(int i=0;i<k;++i)//i表示滑动窗口的最右侧下标
{
while(!q.empty()&&nums[i]>nums[q.back()])
q.pop_back();
q.push_back(i);
}
vector<int>result;
for(int i=k;i<n;++i)//i表示滑动窗口的最右侧下标
{
while(!q.empty()&&nums[i]>nums[q.back()])
q.pop_back();
q.push_back(i);
if(q.front()<=i-k)
q.pop_front();
result.push_back(nums[q.front()]);
}
}