单调队列

单调队列,是一种滑动窗口的优化技巧,用于求滑动窗口的最值。单调队列,其实是一种维护区间性单调序列的数据结构,比如说给出一个区间,我要一直求当前的最小值,而且这个区间一直向右或向左滑动,并且给出5*10^6的数据规模,也就是说几乎就是要O(n)算法时,就可以直接用单调队列了。单调队列的时间复杂度是O(n),而且常数比较小,可谓是一个相当好的数据结构。
那么,单调队列的实现又是怎样的呢?
首先,单调队列肯定是一个队列,可以弹出最老值,加入最新值。其次,因为其的单调性,其主要维护的是这么一个区间内的一个单调序列,所以可以查询最值。下面举个例子,来说明单调队列的构造。如以维护滑动窗口最小值的单调队列为例,数据是[5,2,8,3,4,6,7],滑动窗口的长度是3。
1.加入第一个数据5,单调队列内部为[5],一个元素肯定保持单调上升性。

2.加入第二个数据2,单调队列现在是[5,2],将5和2比较,5>2,则破坏了单调上升性。因为5大于2,所以说5肯定不是区间内的最小值,因此将5弹出,单调队列变为[2]。

3.加入第三个数据8,单调队列变为[2,8],将2和8进行比较,2<8,所以单调上升性仍然维持。
现在单调队列的长度已经达到了滑动窗口的长度,因此下一步窗口将开始向右滑动。

4.首先弹出5,但是5已经被弹出,不用管了。在这里说一下怎样判断最老的元素有没有被删除:只需要比较这个最老的元素是否在队首即可,如果在队首,则删除之,否则就表明已经删除。如果最元素已经因为单调性的无法维持而被删除,则原因肯定是后面存在一个数小于这个最老的数,所以说队列里的数肯定是相应按照坐标顺序的,因此当前单调队列里最老的数无疑就是队首的那个数了。所以只需比较一下即可。说完了就继续,在单调队列里加入3,单调队列变为[2,8,3],将8和3比较,发现8>3,单调上升性无法维持,所以将8弹出。接下来单调队列变为[2,3],将2和3比较,就可以发现单调上升性仍然维持,所以可以停止弹出。

5.接着弹出2,2在队首,所以弹出2。然后加入4,仍然可以维持单调上升性,队列变为[3,4]。

6.弹出8,8不在队首,则不管。加入6,就变为[3,4,6]。

7.弹出3,3在队首,则弹出3。加入7,单调队列就变为[4,6,7]。

看懂了没有?单调队列的原理其实就是不断删除最小并维护序列的单调性,而因为序列的单调性,我们在删除时也可以很方便地删除。不过,这么复杂的操作,其时间复杂度又是怎样呢?我们前面已经说过了,是O(n)。一共有n个元素,每个元素至多删除一次、加入一次,所以时间复杂度就是O(1)*n=O(n)。多么优秀的效率啊!
单调队列的应用范围极广,有一些动态规划也能用这种数据结构来状态压缩、剪枝、转移优化。还有之前的mooo一题,也是在维护一个相对来说的区间滑动最值问题,所以只要想到,就不难做到。实际上,队列和栈也是可以互相联通的。所以不仅有单调队列,更有单调栈。单调栈的应用同样十分广泛,KISS模拟杯0x04中的美丽的序列一题用单调栈就能够被大幅度优化,比起并查集来说常数还小了不少,甚至不用写读入优化就能够过,而写了读入优化,面对这样的数据量也是非常高效!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值