单调队列入门篇

2 篇文章 0 订阅
1 篇文章 0 订阅

单调队列:即单调递减或单调递增的队列,通常处理滑动窗口类的问题

题意大致:有一个大小为 m 的滑动窗口,每次要输出窗口中的最大值

eg:有数组[3 5 2 6 3 9],m为3

单调队列的思想过程:

  滑动窗口                      最大值                        ·状态

【3】 5 2 6 3 9                暂无                滑动窗口内无数据,从队尾排入一数【3】

【3 5 】2 6 3 9                暂无                从队尾排入第二个数,5>3,则3从队头出列【5】

【3 5 2 】6 3 9                   5                  从对尾排入第三个数,窗口内已有过三个数【5 2】

3 【5 2 6】 3 9                   6                   移动窗口,从队尾排入6,6>2,排出2,6>5,排出5【6】

3 5 【2 6 3】 9                  6                     移动窗口,从队尾排入3【6 3】

3 5 2【 6 3 9】                  9                    移动窗口,从队尾排入9,9>3,9>6均排出【9】

看到这里,你可能会想到这样写代码:

for(int i=m;i<n;i++)
{
    int max1=a[i];
    for(int j=i-m+1;j<=i;j++)
    max1=max(max1,a[j]);
    cout<<max1<<endl;
}

这样写没有问题,但是时间复杂度无法想象,仔细想一想,有没有发现i-1个元素都是已经比较过了的,再次比较每次都是重复比较的,那为什么不保存上次比较的最大值呢,这就是单调队列的思想,那现在看看神奇的单调队列是怎么完成的吧:

1、队头出队:滑动窗口移动时,队尾移入一个,队头相应移除一个(h++)

2、队尾入队:新元素滑入窗口时,从尾端插入分两种情况:

(1)直接插入:如果新元素小于队尾元素,直接从队尾插入(++t)

(2)先删后插:如果新元素大于队尾的元素,就要先删除队尾元素(t--)(这里用到循环删除,直到队空或者队尾元素大于新元素)

代码展示:

#include <iostream>
using namespace std;
int main()
{
	int h=0;//头部下标记号
	int t=-1;//尾部下表记号
	int n,m;//数组个数&要限定的范围
	int a[100];//输入数组
	int q[100];//存放下标
	cin>>n>>m;
	for(int i=1;i<=n;i++)//遍历整个数组
	{
		cin>>a[i];
		if(h<=t&&q[h]<i-m+1)//当头部小于等于尾部时,当前下标不在[i-m+1,i]的范围内时,队头出队
			h++;
		while(h<=t&&a[i]>a[q[t]])//当头部小于等于尾部时,当前值>队尾值,队尾出队(做一个循环比较,直到队尾值不再比当前值大为止)
			t--;
		q[++t]=i;//注意前面t多--了,所以这里要++t,不能t++
		if(i>=m)//输出[m,i]的范围内每增一个元素内的最大值,即队头
			cout<<a[q[h]]<<endl;
	}
}

注意:队列用存储元素下标值的方法是便于判断队头出列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LUSAIQUN637

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值