整理一下单调队列的内容。
定义:队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。
单调队列的常用操作如下:
(1)插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再破坏单调性为止,再将其插入单调队列。
(2)获取最优(最大、最小)值:访问首尾元素。
单调队列一道入门级题目,滑动窗口中元素的最值[POJ2823]
题目大意:给定一个大小已知的数组以及一个大小已知的滑动窗口,窗口每个时刻向后移动一位,求出每个时刻窗口中数字的最大值和最小值。
分析:区间长度为k,求区间内的最大值时,考虑第i个数和第j个数,j-i<k,若a[i]<a[j],那么a[i]将毫无用处。因为窗口的移动,a[i]要比a[j]先移出去,无论如何,区间的最大值都不可能是a[i]。这样,考虑构造一个单调递增的队列,存放相应的序号,当a[队尾]>=要入队数据a[i],删除队尾元素;当队头<=i-k时,删除队头元素。
代码及注释如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1100000;
int n,k;
int a[N];
int DanDiao_Que[N]; //单调递减队列(最大),单调递增队列(最小)
int head,tail;//队首队尾
void Min()//递增
{
int i;
int head = 1;
int tail = 0;
for(i = 0; i < k-1; i++)//开始的k个
{
while(head<=tail && a[DanDiao_Que[tail]]>=a[i]) tail--;
tail++;
DanDiao_Que[tail] = i;
}
for(i = k-1; i < n; i++)//移动中的区间
{
while(head<=tail && a[DanDiao_Que[tail]]>=a[i]) tail--;
tail++;
DanDiao_Que[tail] = i;
while(DanDiao_Que[head]< i-k+1) head++;//没到结尾处
printf("%d",a[DanDiao_Que[head]]);//单增的队首即为最小值
printf("%c",i==n-1?'\n':' ');//空格及回车
}
}
void Max()//递减
{
int i;
int head = 1;
int tail = 0;
for(i = 0; i < k-1; i++)
{
while(head<=tail && a[DanDiao_Que[tail]]<=a[i]) tail--;
tail++;
DanDiao_Que[tail] = i;
}
for(i = k-1; i < n; i++)
{
while(head<=tail && a[DanDiao_Que[tail]]<=a[i]) tail--;
tail++;
DanDiao_Que[tail] = i;
while(DanDiao_Que[head]< i-k+1) head++;
printf("%d",a[DanDiao_Que[head]]);//单减的队首即为最大值
printf("%c",i==n-1?'\n':' ');
}
}
int main()
{
scanf("%d %d",&n,&k);//n个数 区间长度k
for(int i = 0 ;i < n; i++)
scanf("%d",&a[i]);//n个数
Min();
Max();
return 0;
}
看了一些树状数组的课件,有点儿懵,继续理解吧。
以上~