算法—单调队列

1.单调队列简介

单调队列是一个数据结构,并不是STL里面的内容。

单调队列为何说单调,因为是队列中的元素始终保持着单增或者单减的特性。

单调队列的核心功能

在每次加入或者删除元素时都保持序列里的元素有序,即队首元素始终是最小值或者最大值

举例,以下面数组为例,数组中的元素插入单调队列中

a[] = {3,1,15,10,7}

假设队列是单调递减的,那么队首元素始终是最大的,五次操作后的队列元素排列情况如下

第 i 次操作插入的元素结果详细过程
3[3]队列里没有元素,3插入到队列中
1[3,1]1小于队头元素,加入到队列中
15[15]

15大于当前元素,且大于队头元素

1,3出队列,15入队列

10[15,10]10小于队头元素,可以加入队列
7[15,10,7]7小于当前元素,可以入队列

数组中五个元素入队列后,排列结果结果为[15,10,7],队头元素为最大值,队列单调递减

2.单调队列的实现及模版

用一道模板题实现以下单调队列

P1886 滑动窗口 /【模板】单调队列 - 洛谷

思路:

1.创建一个双端队列,找最小值为例

2.创建队列后,队列中没有值,1直接插入 

 (注意:插入的是元素的下标,为了判断判断存的元素是否在窗口内)

3.现在3面临抉择,插入3后,如果后面两个数比3大,那么3有可能会是最小值  ,3进队

4.下面出现了-1,-1比队尾元素小,意味着只要-1进队,3永远不可能成为最小,那么就让3出队,同理,1也应该出队 

5.按照这样的规律,我们可以总结出:

当队尾元素大于等于当前元素时 即 a[q.back()] >=a[i]

就让队尾出队  q.pop_back()  再存入当前元素的下标,放到队尾

当队尾元素小于当前元素时 即 a[q.back()] < a[i]

存入当前元素的下标,放到队尾

还要检查队头元素的位置,判断队头是否在窗口内,q.back() - q.front()+1 要小于k 

如果q.back() - q.front()+1 > k   队头元素要出队列   q.pop_front()

最大值同理,仅需改变符号。

当队尾元素小于等于当前元素时 即 a[q.back()] <=a[i]

就让队尾出队  q.pop_back()  再存入当前元素的下标,放到队尾

模版:

#include<iostream>
#include<deque>
using namespace std;

const int N = 1e6+10;
typedef long long LL;
LL n,k;
LL a[N];

int main()
{
    cin>>n>>k;
    for(int i = 1;i <= n;i++) cin>>a[i];

    deque<LL> q;
    for(int i = 1;i <= n;i++)
    {
        while(q.size() && a[q.back()] >= a[i]) q.pop_back();
        q.push_back(i);
        if(q.back() - q.front() + 1 > k) q.pop_front();
        if(i>=k) cout<<a[q.front()]<<" ";
    }
    cout<<endl;
    q.clear();
    for(int i = 1;i <= n;i++)
    {
        while(q.size() && a[q.back()] <= a[i]) q.pop_back();
        q.push_back(i);
        if(q.back() - q.front() + 1 > k) q.pop_front();
        if(i>=k) cout<<a[q.front()]<<" ";
    }
    cout<<endl;
    return 0;
}

3.经典例题

P2251 质量检测 - 洛谷

#include<iostream>
#include<deque>
using namespace std;

const int N = 1e6+10;
typedef long long LL;
LL q[N],A[N];
LL n,m;

int main()
{
    cin>>n>>m; 
    for(int i = 1;i <= n;i++) cin>>A[i];
    
    deque<LL> q;
    for(int i = 1;i <= n;i++)
    {
        while(q.size() && A[q.back()] >= A[i]) q.pop_back();
        q.push_back(i);
        if(q.back()-q.front()+1 > m)  q.pop_front();
        if(i>=m) cout<<A[q.front()]<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值