单调队列(deque和数组模拟)
目录
算法解析
1.什么是单调队列
顾名思义,就是兼具单调性质和队列性质的数据结构
2.优势
如果我们想在一个普通队列中找到一个极值,需要遍历整个队列,时间复杂度为线性O(N)
单调队列因为具有单调性,只需要调用队首或者队尾,时间复杂度为常数O(1)
3.单调队列适用于那些范围
单调队列,其实是单调栈的一个升级plus版本,或者说是具有[l,r]区间性质的单调栈.(注:单调栈一般来说是[0,r]类型的)
4.两个应用
(1)求窗口(区间)的极值
(2)求离一个元素最近的比它小的或者比他大的元素
5.队列常用操作
(1)queue<int> q;
q.clear(); q.pop(); q.front(); q.back(); q.size(); q.empty();
(2)deque<int> q;
q.push_back(); q.push(_front); q.pop_front(); q.pop_back();
q.size(); q.empty(); q.clear();
例题:
解法一: deque双端队列
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 7;
int n, k;
int a[N]; //原数组
int q[N]; //模拟队列,保存下标
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i ++ )
cin >> a[i];
int hh = 0, tt = -1; //head, tail; tail设为-1表示队列为空,没有元素
for(int i = 0; i < n; i ++ ) //找极小值
{
while(hh <= tt && q[hh] < i - k + 1) hh ++ ; //范围大于K
while(hh <= tt && a[i] <= a[q[tt]]) tt -- ;
//while不要忘记加上判断队列是否为空
q[++ tt] = i;
if(i - k + 1 >= 0) cout << a[q[hh]] << " "; //不要输出q[hh]
}
cout << endl;
//这里不需要重置队列,只需要重置下标
hh = 0, tt = -1;
for(int i = 0; i < n; i ++ )
{
while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
while(hh <= tt && a[i] >= a[q[tt]]) tt -- ;
q[++ tt] = i;
if(i - k + 1 >= 0) cout << a[q[hh]] << " ";
}
cout << endl;
return 0;
}
解法二 :数组模拟单调队列
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 7;
int n, k;
int a[N]; //原数组
int q[N]; //模拟队列,保存下标
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i ++ )
cin >> a[i];
int hh = 0, tt = -1; //head, tail; tail设为-1表示队列为空,没有元素
for(int i = 0; i < n; i ++ ) //找极小值
{
while(hh <= tt && q[hh] < i - k + 1) hh ++ ; //范围大于K
while(hh <= tt && a[i] <= a[q[tt]]) tt -- ;
//while不要忘记加上判断队列是否为空
q[++ tt] = i;
if(i - k + 1 >= 0) //if(i - k + 1)会多输出,因为i-k+1可能为负数,但负数也为真
cout << a[q[hh]] << " "; //不要输出q[hh],q是存的数组a的下标
}
cout << endl;
//这里不需要重置队列,只需要重置下标
hh = 0, tt = -1;
for(int i = 0; i < n; i ++ )
{
while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
while(hh <= tt && a[i] >= a[q[tt]]) tt -- ;
q[++ tt] = i;
if(i - k + 1 >= 0) cout << a[q[hh]] << " ";
}
cout << endl;
return 0;
}