单调队列算法模板及应用
文章和代码已经归档至【Github 仓库:https://github.com/timerring/algorithms-notes 】或者【AIShareLab】回复 算法笔记 也可获取。
队列算法模板
例题:滑动窗口
单调队列的应用:求滑动窗口中的最大值和最小值
第一步把新元素插入队尾,第二步把滑出去的元素从队首弹出来。
给定一个大小为 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7]
,k 为 3。
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。
第二行有 n 个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
输出样例:
求最小值的过程相当于维护了一个升序的序列,每次队尾插入的值会使原队尾大于它的值一直弹出,最后输出时就会弹出该区间的最小值。
思路:
最小值和最大值分开来做,都做以下四步:
队首是否出窗口;
解决队尾与当前元素 a[i]不满足单调性;
将当前元素下标加入队尾;(一定要先 3 后 4,因为有可能输出的正是新加入的那个元素;)
如果满足条件则输出结果;
需要注意的细节:
队列中存的是原数组的下标,取值时要再套一层,a[q[]];
算最大值前注意将 hh 和 tt 重置;
此题用 cout 会超时,只能用 printf;
hh 从 0 开始,数组下标也要从 0 开始。
虽然有两个循环,但是时间复杂度是 O(N)的,因为 while 那里判断条件最多执行常数次,比如新加入一个最小值,哪怕一直弹出到队首,队列长度才 k 个,k 是常数,所以 while 最多执行 k 次,合起来就是 O(kN),化简就是 O(N)。
code
版权声明: 本文为 InfoQ 作者【timerring】的原创文章。
原文链接:【http://xie.infoq.cn/article/58ce2d7b728f7b83e2736d5e6】。未经作者许可,禁止转载。
评论