题目
题目链接
滑动窗口求解数组最大最小值
思路分析
RMQ和线段树显然可以做,但是线段树打起来太麻烦了,学点简单的方法总是好的。
单调队列是一种队列(废话)
其中队列的元素保证是单调递增或者是单调递减的
那么队首的元素不就是最小(或最大)的吗?
很明显,当我们在计算区间 的最大值时,是不
是区间 我们之前已经计算过了?那么我们是不是要保存上一次的结果呢(主要是最大值)?
这时,单调队列登场——
单调队列主要有两个操作:删头和去尾
1.删头
如果队列头的元素离开了我们当前操作的区间,那么这个元素就没有任何用了,我们就要把它删掉,也就是说删头是为了去除过期的数据。
扫描二维码关注公众号,回复:
10422452 查看本文章
2.去尾
假设即将进入队列的元素为 ,队列尾指针为 ,
这时我们要比较二者的大小
- :此时q仍然保证着递减性,故直接将 X 插入队列尾
-
:此时,队列递减性被打破,此时我们做一下操作:
① 弹出队尾元素:因为当前的 不但不是最大值,对于以后的情况也不可能比 更优。所以要弹出这好比当前队列尾部的元素是个能力不足的老兵,而新加入的新兵更年轻,更能打,当然不要老兵了
②重复执行①,直到满足 或者队列为空为止
③将 XX 插入队列
对于样例而言:
[1 3 -1] -3 5 3 6 7
q={1},{3},{3,-1} output:3//分别为每次操作的结果
1 [3 -1 -3] 5 3 6 7
q={3,-1,-3} output:3
1 3 [-1 -3 5] 3 6 7
q={-1,-3},{-1,5},{5} output:5
1 3 -1 [-3 5 3] 6 7
q={5,3} output:5
1 3 -1 -3 [5 3 6] 7
q={5,6},{6} output:6
1 3 -1 -3 5 [3 6 7]
q={7} output:7
由于每个元素最多入队一次,出队一次(为什么?),所以时间复杂度为 当然,这题还可以用ST表和线段树来做,但都没有单调队列方便
实现:
由于要对队首和队尾进行维护,所以我们需要使用
双端队列
可以用STL中的deque
,也可以手写
2 d.front():返回的一个元素的引用。
3 d.back():返回最后一个元素的引用。
4 d.pop_back():删除尾部的元素。不返回值。
5 d.pop_front():删除头部元素。不返回值。
6 d.push_back(e):在队尾添加一个元素e。
7 d.push_front(e):在对头添加一个元素e。
方便起见,我们在单调队列中维护数组的下标,省去了使用pair
或者结构体的繁琐:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<deque>
#include<vector>
#include<algorithm>
using namespace std;
int n,m;
int a[1000001];
int b[1000001];
int c[1000001];
deque<int> qx,qn;
int cnt;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
while(!qx.empty()) qx.pop_front();
while(!qn.empty()) qn.pop_front();
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
{
while(!qx.empty()&&a[i]>=a[qx.back()])//判断是否新加得数比前面得大,大的话我们就要把最大值放最前,
qx.pop_back();
qx.push_back(i);
while(!qn.empty()&&a[i]<=a[qn.back()])
qn.pop_back();
qn.push_back(i);
if(i>=m-1)
{
while(!qx.empty()&&qx.front()<=i-m) qx.pop_front();//我们把出了区间的数踢出队列
b[cnt]=a[qx.front()];
while(!qn.empty()&&qn.front()<=i-m) qn.pop_front();
c[cnt++]=a[qn.front()];
}
}
for(int i=0;i<cnt;i++)
{
if(i==0)
printf("%d",c[i]);
else printf(" %d",c[i]);
}
printf("\n");
for(int i=0;i<cnt;i++)
{
if(i==0)
printf("%d",b[i]);
else printf(" %d",b[i]);
}
printf("\n");
}
}