单调队列的实现与应用

来源:互联网 发布:网吧会员软件 编辑:程序博客网 时间:2024/06/05 09:55

                                       单调队列

单调队列引入:

首先看一个例题:

  给你一个长度为N的数组,一个长为K的滑动框从最左端滑到最右端,你只可以看到框内的K个数,每次框向右移动一格。请你找出框在各位置的最小值。

样例输入:

8 3

1 3 -1 -3 5 3 6 7

样例输出:

-1 -3 -3 -3 3 3

代码实现:

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;int n,m,i,j,k,l,h,t;int a[100000],q[100000],time[1000001];/*a中存储的就是数组中元素的值,q中存储的是单调队列该位置所对应的数组中元素的值,time是保存单调队列该位置所对应的数组中元素进队的时间*/int main(){cin>>n>>k;for (i=1;i<=n;i++) cin>>a[i];h=1;for (i=1;i<=n;i++){while (i-time[h]>=k&&(h<t)) h++;队首元素超出K区间边界,需要剔除。while (t>0&&t>=l&&q[t]>a[i]) t--;//从队尾向前扫描,直到找到一个比a[i]小的。此时相当于把后面的剔除了。因为已经可以确定那些值不会是ans。t++;q[t]=a[i];time[t]=i;if (i>=k) cout<<q[h]<<' ';}} 

这个例题充分说明了单调队列是干什么的?来查找不断移动的K区间内的最小(大)值(动态规划中应用广泛)。

单调队列的维护:

    1.这里采用了最直接的方法:普通队列实现缓存数组。

    进队出队都是O(1),一次查询需要遍历当前队列的所有元素,故O(n)

    2.还可以用堆实现缓存数组

        堆顶始终是最小元素,故查询是O(1)(对q数组用堆来维护)

        而进队出队,都要调整堆,是O(log(n))

单调队列的过程

     查询单调队列的队头一定最小值,所以查询为O(1)

     进队进队时,将进队的元素为a[i],从队尾往前扫描,直到找到一个不大于a[i]的元素f[t]a[i]放在f[t]之后,舍弃f[t]之后的所有元素;如果没有找到,则a[i]放在队头(该值当前最小)

     出队出队时,将出队的元素为q[h],从队头向后扫描,直到找到一个‘未老’的元素(即在给定的范围当中),舍弃该元素之前所有的。

     每个元素最多进队一次,出队一次,摊下来仍然是 O(1)

单调队列实际上是具有单调性的子序列,但这并不是一个真正意义上的队列,在进队和出队时对某段进行反复操作。但是却具有队列的特性。

    单调队列和二分,线段树一样是用来优化的一种数据结构。在动态规划中应用较广泛

0 0
原创粉丝点击