经典的单调队列的题目,不刷必后悔
思路解析:
显然我们可以可以写一个简单的O(nk)的算法,但是会超时.考虑到本题是需要维护一个有限制的最大值,我们使用单调队列这个数据结构优化这个过程.
单调队列里面存储元素的下标,队头是当前的窗口最大值,按照递减排列,显然从队头到队尾的序列是 0...len-1 的一个子序列
主要操作:
1.每次我们判断当前队头是否还在范围内, 即是 i-dq.front() > k
2.之后维护当前窗口的最大值,也就是 把当前元素和队尾比较,删除从队尾开始小于等于当前元素的那些,然后把当前元素加入,即是dq.push_back(i)来维护当前窗口的最大值.
经过上面两种操作我们就可以在O(1)的时间复杂度中找到当前窗口的最大值了
题外话:
其实我们也能看成单调栈/单调队列的单调性并不是我们的要求,而是我们为了保证之后能在O(1)时间内,正确找到窗口最大值而不得已把它变成了单调的,所以我们其实只能保证一个最大最小值,剩下的就看题意和逻辑了
代码如下:
class Solution {
public:
//deque 有双端操作
//push_front push_back pop_front pop_back begin end insert front back
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> dq;
vector<int> ans;
for(int i=0;i<nums.size();i++){
//1. 入
while(!dq.empty() && nums[i]>=nums[dq.back()]){
dq.pop_back();
}
dq.push_back(i);
//2. 出
if(dq.front() <= i-k){
dq.pop_front();
}
//3. 记录答案
if(i+1 >= k) ans.push_back(nums[dq.front()]);
}
return ans;
}
};
note:
其实从这个题目我们也可以看成单调队列的简单使用模板:
for(int i=0;i<nums.size();i++){
//1. 入
while(dq.size() && nums[i]比较nums[dq.back()]){
dq.pop_back(); //在pop_back处保证单调性
}
dq.push_back(i);
//2. 出
while(dq.size() && 限制条件){
dq.pop_front(); //pop_front处满足限制条件
}
//3. 记录答案
}
// 1. 2. 会视情况互换
// 也有的是使用left++ ,而把i当作right , 当i每次+1, 看看left需要往左走几步满足
// 并且在这个时候pop_front()
// 多用于同时使用两个单调队列维护最大最小值的情况