数据结构 - 单调队列入门

3 篇文章 0 订阅
2 篇文章 0 订阅

今天我们来聊聊单调队列
单调单调,就代表这种队列具有单调性
而单调性又分为单调递增与单调递减
举个栗子:

1 4 5 6 7 8 10

这就是一个单调递增的序列

1 4 6 5 7 8 10

就不是
而这和我们的“单调队列”又有什么关系呢?
单调队列的作用就是将区间最小或者最大值维护在队首
那么我们要怎么维护呢?
给同学们思考1年
大家解放自己的脑洞,试想一下如果一个队列从队首到队尾单调递减,那么队首不就是区间最大值了吗?
没错!这就是“单调队列”的一个核心思想
于是乎,构建单调队列的方法也非常明显了
比如

1 7 5 4 8 2 6

我们怎样创建一个区间最大值单调队列呢?
我们需要保证队列单调递减
工程开始:
i=1,a[i]=1 ,队首为0,0<1,清空队列,1入队,队列为 ( 1 )
i=2,a[i]=7 ,队首为1,1<7,清空队列,7入队,队列为 ( 7 )
i=3,a[i]=5 ,队首为7,7>5,队尾为7,7>5,5入队,队列为 ( 7 , 5 )
i=4,a[i]=4 ,队首为7,7>4,队尾为5,5>4,4入队,队列为 ( 7 , 5 , 4 )
i=5,a[i]=8 ,队首为7,7<8,清空队列,8入队,队列为 ( 8 )
i=6,a[i]=2 ,队首为8,8>2,队尾为8,8>2,2入队,队列为 ( 8 , 2 )
i=7,a[i]=6 ,队首为8,8>6,队尾为2,2<6,2出队,6入队,队列为 ( 8 , 6 )
工程结束。
大家可以发现,在每一个过程中,我们都对队首队尾进行了判断,保证了队列的单调递减,成功维护了队首为区间最大值

例题 P1886 滑动窗口

原题链接
0、首先读题,发现这题要维护区间的最大值与最小值,判断算法为单调队列
1、创建 4 个队列maxn,minx,x,y,分别用于存储区间最大最小值及序号,推荐使用 STL 的 deque
2、从数组的第一个元素开始遍历,先对前 m-1 个元素创建单调队列,再继续创建单调队列
3、如果当前元素比 maxn 队首元素要大,说明队中的所有元素都比当前元素小,清空队列,当前元素入队
从队尾开始将比当前元素小的元素全部弹出
如果队首的序号超出本区间,弹出队首
minx 队列的做法与 maxn 相似,大家可以试着举一反三
代码仅供参考:

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005],ans1[1000005],ans2[1000005];
deque <int> maxn,minx,x,y;//注意用deque
inline int read()
{
    int x=0,w=1;
    char ch=0;
    while(ch<'0' || ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') x=(x << 3)+(x << 1)+ch-'0',ch=getchar();
    return x*w;
}
//读优 
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<m;i++)
    {
        while(!maxn.empty() && a[i]<=maxn.back()) maxn.pop_back(),x.pop_back();
        while(!minx.empty() && a[i]>=minx.back()) minx.pop_back(),y.pop_back();
        maxn.push_back(a[i]);
        x.push_back(i);
        minx.push_back(a[i]);
        y.push_back(i);
    }//重要,先对前 m-1 个元素创建单调队列
    for(int i=m;i<=n;i++)
    {
        while(!maxn.empty() && a[i]<=maxn.back()) maxn.pop_back(),x.pop_back();
        while(!minx.empty() && a[i]>=minx.back()) minx.pop_back(),y.pop_back();
        maxn.push_back(a[i]);
        x.push_back(i);
        minx.push_back(a[i]);
        y.push_back(i);
        while(!maxn.empty() && i-x.front()>=m) maxn.pop_front(),x.pop_front();
        while(!minx.empty() && i-y.front()>=m) minx.pop_front(),y.pop_front();//单调队列的维护
        ans1[i]=maxn.front();
        ans2[i]=minx.front();//记录答案
    }
    for(int i=m;i<=n;i++) printf("%d ",ans1[i]);
    printf("\n");
    for(int i=m;i<=n;i++) printf("%d ",ans2[i]);//输出即可
    return 0;
}

原创 By Venus
写的不好大佬轻喷

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值