什么是单调队列
单调队列,即单调递减或单调递增的队列。使用频率不高,但在有些程序中会有非同寻常的作用。
单调队列的作用
不断地向缓存数组里读入元素,也不时地去掉最老的元素,不定期的询问当前缓存数组里的最小/最大的元素。
最直接的方法:普通队列实现缓存数组。
用堆实现缓存数组
运行耗时
堆顶始终是最小/最大元素,进队出队都是O(1)。
一次查询需要遍历当前队列的所有元素,故O(n)。
而进队出队,都要调整堆,是O(log(n))。
操作
进队时,将进队的元素为e,从队尾往前扫描,直到找到一个不大于e的元素d,将e放在d之后,舍弃e之后的所有元素;如果没有找到这样一个d(即已入队的元素全部比e大),则将e放在队头(此时队列里只有这一个元素)。
出队时,将出队的元素为e,从队头向后扫描,直到找到一个元素f比e后进队,舍弃f之前所有的。(实际操作中,由于是按序逐个出队,所以每次只需要取队头元素出队即可)。
每个元素最多进队一次,出队一次。
模板题
题目描述
给一个数组,该数组中有n个元素,要求找出每连续的m个数中的最大值和最小值。(即找出每个连续m个区间长度中的最大值和最小值。)
输入
有一组测试样例。
第一行有两个数n和m,n代表数组的长度,m代表连续区间的长度。
第二行输入n个数,表示该数组中的n个元素。
输出
每连续的m个数中的最大值和最小值。
解题思路
这个是典型的固定m区间的单调队列。套用的本质思想是:
如求最小值: 考虑这样的一个问题,在某个区间当中如果存在某两个元素A,B,满足A的下标小于B的下标,A的值大于B的值,那么A这个数就可以删掉,不再考虑。
例如有数列 1 5 2 3 4
很显然 5的下标为 2, 2的下标为3 2<3,但 5>2 不符合单调递增
故删去5,数列成为 1 2 3 4 符合单调递增
如求最大值: 考虑这样的一个问题,在某个区间当中如果存在某两个元素A,B,满足A的下标小于B的下标,且A的值小于B的值,那么A这个数就可以删掉,不再考虑。
例如有数列 6 4 5 2 1
很显然 4的下标为 2, 5的下标为3 2<3,且 4<5 不符合单调递减
故删去4,数列成为 6 5 2 1 符合单调递减
具体的操作是:从加入第m个数开始,每插入做一次队列单调性更新:
但是可能会遇到正阳一种情况:
例如有数列 1 5 4 6 9 10. 要求m值为3
整理后得到 1 4 6 9 10
第一个连续m区间[1,3]时 单调队列v存的是(存下标) 1 3.
故最小值是队首a[v[1]]=a[1]=1
----------
第二个连续m区间[2,4]时 单调队列v存的是(存下标) 1 3 4.
此时队首依然是a[v[1]],但很显然v[1]已经不再区间[2,4]中了.
故此时我们需要把队首后推一位,直至在区间[2,4]内即a[v[2]]=a[3]=4
主要步骤就是
删队尾【单调性】,入队,删队首【下标范围m以内】,输出队首【即最值】。
代码(即单调队列模板)
#include <iostream>
#include<cstdio>
using namespace std;
const int Max = 1000004;
int n,m,a[Max];//定义输入的三个量
int v[Max],h,t;
//数组v代表单调队列,存放数组a的下标,h代表队首,t代表队尾
void getmin()//单调递增
{
h = 1;t = 0;//代表队列的头和尾
for(int i = 1; i <= n; i++)
{
while(h <= t && a[v[t]] >= a[i]) t--;
//如果要插入的数比队尾的数小,将对位的取代
v[++t] = i;
if(i>=m) //前m个数一定是在第一个m区间的
{
while(v[h] < i-m+1) h++;//此时的最小值不再m区间内,队首后移
printf("%d ",a[v[h]]);//输出队首元素,即最小值
}
}
cout << endl;
}
void getmax()//单调递减
{
h = 1;t = 0;
for(int i = 1; i < m; i++)
{
while(h <= t && a[v[t]] <= a[i]) t--;
v[++t] = i;
}
for(int i = m; i <= n; i++)
{
while(h <= t && a[v[t]] <= a[i]) t--;
v[++t] = i;
if(i>=m) //前m个数一定是在第一个m区间的
{
while(v[h] < i-m+1) h++;
printf("%d ",a[v[h]]);
}
}
cout << endl;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i];
getmin();
getmax();
}