单调队列(deque和数组模拟)

目录

算法解析

1.什么是单调队列

2.优势

3.单调队列适用于那些范围

4.两个应用

5.队列常用操作

例题:



算法解析

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();

参考:[投稿]2019年4月11日单调队列讲义 - AcWing


例题:

154. 滑动窗口 - AcWing题库

解法一: 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;
}

例题:134. 双端队列 - AcWing题库

posted @ 2022-05-05 08:41  光風霽月  阅读(227)  评论(0编辑  收藏  举报