滑动窗口之单调队列
1.滑动窗口
普通情况下是一个固定长度的队列,该队列在一个数组中不断移动,移动的过程中,数组的元素不断进出该队列,因此,可以将队列的移动抽象成一个滑动的窗口。
2.单调队列
常用于解决滑动窗口的最值问题,即在队列移动过程中,每个窗口对应的最值。
为了能够在常数时间复杂度内得到最值,因此队列本身应该是有序的,最值只需要获取队列首部或尾部的元素即可,从而引出单调队列。
在队列移动过程中,如何使该队列单调并且只包含在窗口内的元素只需要注意处理两个部分,下面用如何构造单调递减的队列来举个栗子
1.数组元素进入队列
我们可以注意这样一个核心思想,如果队列中新进来的元素为a,已经在队列的元素为b。在队列不断向右移动的过程中,新进来的元素一定比后面的元素更晚出队列,那么当a进入队列后,只可能是b先出队列,因此,如果a>b,那么当a进入队列后窗口的最大值一定不会是b,而且会一直持续到b出队列,因此在a进来队列后,b已经是没用的元素了,可以直接pop掉。通过上述操作可以将其它已经在队列中并且比a小的元素移除,从而保证队列中的元素都是比a大的元素,再将a加入队列中,即可实现队列中的有序性。
2.数组元素出队列:
通过第一个处理,已经实现了队列单调递减了,而第二个操作的目的实现队列中只包含在当前窗口中的元素。
在求当前窗口的最值时,即使队列已经有序了,最值只需要取队列首部或尾部元素即可。
但是,取出的最值可能已经是窗口外的元素了,所以在取出该最值后,需要判断其是否在窗口内,如果不在,那么需要将其出队列,上述操作循环进行,即可实现将队列内的元素保持在窗口内,直到队列中首部或尾部的最值在窗口内结束循环,从而保证队列的单纯性
代码如下:
//nums为待遍历数组,k为滑动窗口大小
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
//双端队列实现滑动窗口
deque<int> ant;
//ans保存每个窗口的最大值
vector<int> ans;
if(nums.size()==0) return ans;
//开始滑动
for(int i=0;i<nums.size();i++){
/*从后往前遍历队列,取出队列中小于nums[i]的数,
保证队列的单调性*/
while(!ant.empty()&&nums[i]>nums[ant.back()]){
ant.pop_back();
}
//将新进入队列的元素下标放入队列中
ant.push_back(i);
/*从前往后,取出队列中不在窗口中的数,
保证队列的单纯性*/
while(i-ant.front()>=k){
ant.pop_front();
}
//队列达到窗口大小后才将队列最大值存入数组中
if(i>=k-1){
ans.push_back(nums[ant.front()]);
}
}
return ans;
}