滑动窗口
滑动窗口是一种想象出来的数据结构,窗口有左边界L和右边界R。
在数组或者字符串或者一个序列上,记为S,窗口就是S[L…R]这一部分,L往右滑动意味着有样本滑出窗口,R往右划动意味着有样本滑入窗口,L和R都只能往右滑动。
窗口定义
- R边界向右移动,数组中的数从窗口右侧进入窗口
- L边界向右移动,数组中的数从窗口左侧出窗口
- 左边界不能到右边界的右侧
如何找到动态窗口中的最大值
- 准备一个双端队列,数据可以从头部进头部出,也可以从尾部进尾部出。
- R++时,将数据从双端队列的尾部插入,此时队列为空,可以直接将数组对应元素的索引直接插入。
- 双端队列从头到尾按照从大到小的顺序保持,所以当R++,即窗口右边界往右滑动时,窗口内加入新的数,此时需要判断队列的末端的数字是否大于当前数,如果末端数字大于当前数,因为仍然保持大小顺序,所以可以直接将数插入到队列末端。
- 如果队列末端数字索引数组的值小于或等于当前数,若直接插入会违反队列中的大小顺序,所以需要将队列末端的数弹出,直到队列为空(可以直接插入)或者末端的数大于当前数(满足大小顺序)。
- L++时,即左边界往右移动,此时需要将队列中那些不在窗口中的数剔除,所以当左边界向右移动时,取出队列的头部元素判断是否等于右边界减去窗口宽度的值(因为此时的左边界是R-W+1),若等于,说明队首元素已经过期,需要弹出。
对比遍历法求最大值,分析算法复杂度
L和R需要滑过整个数组,每个位置最多进入一次双端队列,出去一次(两种情况:一种是当前下标过期,从头部出去;另一种是遇到了大于这个数的数,从尾部弹出)。整个过程的时间复杂度O(N),查询某个时刻最大值O(1)。
题目联系leetcode239
思路:维护一个滑动窗口,将双端队列首部的元素不断弹出到结果数组中
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int R=0;
int N=nums.size();
deque<int>dq;
vector<int>result(N-k+1,0);
int index=0;
for(int R=0;R<N;R++){
while(!dq.empty()&&nums[dq.back()]<=nums[R]){
dq.pop_back();
}
dq.push_back(R);
if(dq.front()==R-k){
dq.pop_front();
}
if(R>=k-1){
result[index++]=nums[dq.front()];
}
}
return result;
}
};