题意:
给定一个长度为n的序列,有一个长度为k的滑动窗口,窗口从左向右依次移动,求每次移动时窗口中的最大值。
算法实现:
1.有一种很常见的算法,时间复杂度为O(n*k)的算法,线性遍历每个数再求每个滑动窗口中的最大值,、但当k很大的时候非常耗时。
2.还有一中就是单调递减队列,可以把时间复杂度优化到接近O(n),既然是单调队列,那我们就要定义一个队列,而且还要是双端的。下面我们模拟一下。
假定这个序列的k为3,那我们从第一个元素开始访问,这个时候我们有一个空的双端队列que。我们先把第一个元素及下标入队,队列为{(1,1)},然后开始访问第二个元素,把第二个元素的值与第一个元素的值比较,如果小于则往队列后面加,否则将他前面的一个元素删除,直至遇到比它大的为止,然后将它加入队列。这个队列中就变成了{(3,2)}。继续操作,队列变成{(3,2),(-1,3)}、{(3,2),(-1,3),(-3,4)}、{(3,2),(-1,3),(-3,4),(-4,5)},而这时,我们可以看到队列中的元素个数大于了k,而这时我们应该判断队首元素下标是否小于i-k+1,如果小于则删除队首元素。依照此操作重复进行,直到整个序列遍历完成。在我们入队的时候其实可以只入队下标,用下标来查找元素。
代码如下:
#include<iostream>
#include<cstdio>
#include<deque>
using namespace std;
int n,k;
int cnt = 0;
int a[100002];
int id[100002];
int ans[100002];
deque<int>que;
void getmax(){
for(int i = 1;i <= n; i++){
while(!que.empty() && a[que.back()] <= a[i])que.pop_back();//比较并删除元素
que.push_back(i);//添加元素
while(!que.empty() && que.front() < i-k+1)que.pop_front();//检查是否超出k
ans[cnt++] = que.front();//记录答案
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i = 1;i <= n; i++)scanf("%d",&a[i]);
getmax();
for(int i = k-1;i < cnt; i++)printf("%d ",a[ans[i]]);//输出要从k-1开始输出。
}