关于单调栈

文章介绍了单调栈的概念,将其与单调队列进行了对比,并通过一个具体的问题——求解队列中每个人能看到其他人发型总和,展示了单调栈的使用方法和优势。利用单调栈可以高效地找出每个数字右边第一个大于它的数字,从而解决题目。文章提供了伪代码和实际代码实现,并解释了为何在队尾放置一个无限大值来确保所有数字都被处理。
摘要由CSDN通过智能技术生成

单调栈,顾名思义,就是一个栈中的数据是具有单调性的(从小到大、从大到小)。

上次写单调队列时,我用了一句话来描述单调队列:

当一个人比你小还比你强,你就该考虑退役了 ! \large\text{当一个人比你小还比你强,你就该考虑退役了 !} 当一个人比你小还比你强,你就该考虑退役了 

−   W h e n   s o m e o n e   i s   y o u n g e r   t h a n   y o u   a n d   b e t t e r   t h a n   y o u ,   i t ′ s   t i m e   t o   c o n s i d e r   r e t i r e m e n t !   − \small{-\ When\ someone\ is\ younger\ than\ you\ and\ better\ than\ you,\ it's\ time\ to\ consider\ retirement!\ -}  When someone is younger than you and better than you, its time to consider retirement! 

(详见 \footnotesize\text{(详见} (详见 单调队列 \footnotesize\text{单调队列} 单调队列 ) \footnotesize) )

现在要写单调栈,其实这句话也适用于单调栈,而他两的区别就在于:

  • 你可以把单调队列看作一个定区间,队列里的数据必须存在于这段区间内,通过队首极值)得出答案。

  • 而单调栈一般是在新数据入栈时,与出栈的数据进行相处理,最后综合处理过程得出答案。

注:与单调队列一样,单调栈存的也是下标


伪代码:

stack<int> st;
for (遍历这个数组){
	while (栈不为空 && 栈顶元素比当前元素弱){
		栈顶元素出栈;
		更新结果;
	}
	当前数据入栈;
	
}
得出结果

光看伪代码,大部分人都会说,我会写,但我该怎么用呢


请结合题目食用:

题目描述:有 n 个人排成一队,每个人都是向右看的,高的人可以看到矮的人的发型,求所有人能看到其他人发型总和是多少。

输入:
4
4 3 7 1
   
输出:
2

通过观察,我们可以发现实际上题目转化为:找当前数字向右查找的第一个大于他的数字之间有多少个数字,最后在累加起来即可。

朴素算法: O ( n 2 ) O(n^2) O(n2)

这题我们只需要知道每个数字右边第一个大于他的数字在哪就行了,而且每个数字找到比它大的数字后就不再用得着了。因此我们考虑使用单调栈

代码:

stack<int>st;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
a[n+1]=INT_MAX;
for(int i=1;i<=n+1;i++){
	while(!st.empty()&&a[st.top()]<=a[i]){
		int t=st.top();
		st.pop();
		ans+=(i-t-1);
	}
	st.push(i);
}
cout<<ans;

这个代码的核心部分就在 while 循环里:

把新数字压入栈中时,如果他比当前对比的值大,那么他就是这个对比值右边第一个比对比值大的数。别忘记单调栈里存的是下标,,此时已知两者下标,求出中间有几个数字便轻而易举了。然后再把当前对比值弹出(反正不会再用到它了)。

看到这个代码,想必有的同学会问:“为什么要在队尾放个无限大?”

因为要想知道总和,必须保证每个数字右边的数量我们都知道,既然如此,就必须使得最后每个数都被弹出栈。那么最后设置一个无限大,这样就可以使得每个数字都被弹出。

最后用个 a n s ans ans 累加一下就行了。


小结:

单调栈并不难,只是需要结合实际情况,确定入栈顺序和结果的更新即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值