单调递减队列

来源:互联网 发布:进销存系统数据库设计 编辑:程序博客网 时间:2024/06/02 07:31
题意:

给定一个长度为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开始输出。}




0 0
原创粉丝点击