遇到过一种比较特殊的队列,以前我习惯叫它“双端队列”,可能是当时的误解,先假想一下如下的情况:
有一堆元素,每个元素有一个关键值key,一个代表位置的pos。对于一个给定的位置p,我要找与这个位置距离不超过k的最小(大)的值。但是还有一个重要限制,这堆元素得一个一个处理和查询,先处理的元素的pos必须比后处理的元素的pos要小。就是说,第一个元素处理,第二个元素处理。。。第i个元素处理,这时候如果要查询,只能查询跟第i个元素距离为k的最小(大)的前面处理过的元素,我姑且定位为这种查询为“立即查询”吧。这里有点不好理解,或者说,这么苛刻的条件,这种结构会有什么用武之地,慢慢来,后面你就会明白。
我继续沿用“双端队列”来称呼我所要表达的这种结构,“双端队列”支持两种操作,一是插入,二是出队列,出来的这个元素是和给定的一个pos值不超过k距离的最小(大)的元素。
下面以支持弹出最小元素为例,最大类似
1.基本结构:
令元素为E(key,pos),表示关键字为key,位置为pos的元素;令查询Q(pos,k),查询前面出现的,跟pos位置不超过k的最小元素。
2.插入操作:
队列中将维护一个有序单调非降的序列,当插入元素E(key, pos)时,按key二分查找,查找的落点将落在>key的第一个,如:
que(key):1 2 3 4 7
如果插入3,那么落点是4这个位置,然后修改队尾指针r,使得队列调整为que(key): 1 2 3 3,也就是说4,7都被删除了。为什么呢?因为我后面如果有查询(出队列),我要找最小值,而且,根据前面我们说的必须条件,前面先处理的元素的pos要比我当前小,也就是说4,7的pos比后面的3小,故后面的查询Q(pos,k),无论是pos还是key,4,7比新来的3都没有优势,不可能是他们,所以删除也无妨。
这里f是队列的头位置,而r是队列的尾位置的下一个。
3.出队列操作(查询)
出队列很简单,因为已经是一个有序非降的序列,所以越前面的元素越小,但是,可能其距离不满足要求。这时有个减小队列的维护,如果前面的元素不满足条件,那么这些元素都可以从队列中删除了,因为后面的查询操作将更加不能满足距离要求(还记得必须条件吗)。
我将给一个列子来说明这个“双端队列”的一个应用:
http://acm.pku.edu.cn/JudgeOnline/problem?id=2823
这道题目的意思是:给定一个数组,如[1 3 -1 -3 5 3 6 7],给定一个窗口m=3,窗口从左到又移动,输出每次窗口中的最小最大值。
该例子
[1 3 -1] -3 5 3 6 7,最小-1,最大3
1 [3 -1 -3] 5 3 6 7,最小-3,最大3
等等。。。
其实就是每次查询跟第i个元素距离不超过m的最小(大),在这里每个元素的key是其值,pos是出现的下标。满足pos越后面越大,而且每次都是“立即查询”。故可用“双端队列”求解。
其他在一些DP类中,也很有可能用到它来做优化,08年北京赛区Regional中就有一个又此结构优化的DP的题目,具体忘记了。
下面是pku2828的代码: