单调栈、单调队列

本文详细介绍了单调栈和单调队列的概念及其在算法题中的应用。通过具体实例,如AcWing830和AcWing154,阐述了如何利用单调栈和单调队列优化算法,降低时间复杂度,实现高效编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

单调栈

AcWing 830. 单调栈
在这里插入图片描述
1、暴力做法:外层循环枚举每一个元素 x,内层循环找到这个 x 的左边第一个比它小的数。在求 x 的左边第一个比它小的数,是将 x 左边所有的数全部入栈,从栈顶开始找到第一个比它小的数,找到后停下来。

2、在暴力做法下进行优化。我们发现,如果 a1 >= a2,那么当找 x 的左边第一个比它小的数时, 如果 i < j,且 ai >= aj,ai 一定不是答案,那么 ai 是可以删掉的。删除后栈内元素就是一个单调的序列了,所以叫单调栈
为了维护这个单调性质,我们在插入 x 前,循环判断如果栈顶元素 >= x,栈顶元素出栈即可,这样最后的栈顶元素就是 < x 的左边第一个数了。

#include<iostream>
using namespace std;
const int N = 1e5+10;
int stk[N], n;
int tt;
int main()
{
	cin >> n;
	for(int i=0; i<n; i++)
	{
		int x;
		cin >> x;
		while(tt && stk[tt] >= x) tt--; //栈顶元素出栈
		if(tt) cout << stk[tt] <<" ";
		else cout <<"-1 ";
		
		stk[++tt] = x; //x入栈
	}
}

我们发现每个元素最多只能进栈一次,也最多只能出栈一次,所有元素最多执行 2*N 次,时间复杂度O(N)


单调队列

AcWing 154. 滑动窗口
在这里插入图片描述

1、暴力做法:遍历队列中的 k 个值,总共需要 O(N*k) ,超时。

2、在暴力的基础上优化。我们发现,在当前窗口,如果 i < j,且 ai >= aj,那么 ai 是永远不含当作最小值的,我们可以直接将 ai 删掉,此时的队列元素就是一个单调递增的序列了,因此叫单调队列
为了维护单调这个性质,我们在队尾插入 x 之前,循环判断如果队尾元素 >= x,将队尾元素删除即可。这样队头元素就是当前队列的最小值了。

#include<iostream>
using namespace std;
const int N = 1e6+10;
int n,k;
int a[N], q[N];

int main()
{
	cin.tie(0);
	cin >> n >> k;
	for(int i=1; i<=n; i++) cin >> a[i];
	
	int hh = 0, tt = -1;
	for(int i=1; i<=n; i++)
	{
	    //判断队头是否需要滑出窗口,保证窗口内有 k 个元素
		if(hh <= tt && i-k+1 > q[hh]) hh++; //队头出队
		while(hh <= tt && a[q[tt]] >= a[i]) tt--; //删除队尾
		q[++tt] = i; //x入队
		
		if(i >= k) cout<< a[q[hh]] << " "; //数大于 k 个才需输出
	}
	cout << endl;
	
    hh = 0, tt = -1;
	for(int i=1; i<=n; i++)
	{
		if(hh <= tt && i-k+1 > q[hh]) hh++;
		while(hh <= tt && a[q[tt]] <= a[i]) tt--;
		q[++tt] = i;
		
		if(i >= k) cout<< a[q[hh]] << " ";
	}
}

由于每个元素最多进队一次,出队一次,所有元素最多执行 2*N 次,时间复杂度O(N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值