单调队列

简单来说,单调队列是用来解决这样的问题的:
实现一个目标容器buffer,支持3种操作:
不断地向buffer里读入元素、
时不时会去掉当前buffer里的最老的元素
不定期地询问当前buffer里的最小元素。

实现一个buffer,使得上面的操作的代价尽可能小。

而单调队列,是实现该buffer的一个好方法。

为每个读入到buffer的元素封装一个专门的结构体:
struct Element {
    int time;  // 这是该元素被读入buffer时的时间戳,一般从0开始
    int val;    // 该元素的值
};


单调队列,是一个队列,按照从队头到队尾的方向,并且满足2个性质:
(a)元素的time字段依次增加,也就是说,元素是从旧到新的
(b)元素的val字段依次增加,也就是说,元素是从小到大的


我们先考虑一下用普通的队列是怎么实现目标buffer的。
假设Q是一个普通的双向队列,支持push_back, pop_back, push_front, pop_front
然后我们不断地往Q的尾巴push元素。那么:
要插入一个元素的时候,就push_back那个元素。时间为O(1)
要删除最老元素的时候,只需要pop_front。时间为O(1)
要询问当前最小值的时候,就遍历一次队列里的所有元素。时间为O(n)

考虑一下如何改进。
当我们往Q的尾巴插入一个元素x的时候,有一件事我们是可以马上确定的:
插入x前,对于任意y属于Q,如果y.val >= x.val,那么,对于插入x之后的任意一个询问,问题的答案,都绝对不会是y,因为x既比y小,又比y要新。

也就是说,每当我们插入一个最新元素x到Q的尾巴的时候,那些比x大的元素都没有生存价值,因为他们永远不会成为之后的询问的答案,他们存在于这个世界上唯一可以做的事情就是等待某个时候被删除。
既然这样,当我们插入元素x的时候,我们可以从尾巴往头部方向扫描,把那些大于x元素都先pop出来,反正他们永远都不会成为询问的答案。最后,再插入x到尾部。这样子,性质(a)就得到维护了。

当我们需要删除所有在某个时间t之前插入的一些老元素时,只需要从队头往尾巴方向扫描,把插入时间在t之前的元素都pop出来就行了。

转载于:https://my.oschina.net/mustang/blog/60042

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值