单调栈原理及应用 详解 附各种类型的题目练习

本文详细介绍了单调栈的概念,实现方式及其在算法题中的三种典型应用:求右侧最近较大值、求子序列最大乘积和求子序列最小值乘以和的最大值。并通过具体题目示例,展示了如何利用单调栈提高算法效率。

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

转载:原文链接: https://blog.youkuaiyun.com/zuzhiang/article/details/78134247

 

定义

单调栈,顾名思义,是栈内元素保持一定单调性(单调递增或单调递减)的栈。这里的单调递增或递减是指的从栈顶到栈底单调递增或递减。既然是栈,就满足后进先出的特点。与之相对应的是单调队列。

 

 

实现

例如实现一个单调递增的栈,比如现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。

 

10入栈时,栈为空,直接入栈,栈内元素为10。

3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。

7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。

4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。

12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。

 

至于代码的实现我觉得还是必须对应着题目去体会,也没有太死板的模板,下面只给出伪代码吧。


 
  1. /*
  2. * 本伪代码对应的是单调递减栈
  3. *共n个元素,编号为0~n-1
  4. */
  5. while(栈为空) 栈顶元素出栈; //先清空栈
  6. a[n]=- 1;
  7. for(i= 0;i<=n;i++)
  8. {
  9. if(栈为空或入栈元素大于等于栈顶元素) 入栈;
  10. else
  11. {
  12. while(栈非空并且栈顶元素大于等于入栈元素)
  13. {
  14. 栈顶元素出栈;
  15. 更新结果;
  16. }
  17. 将最后一次出栈的栈顶元素(即当前元素可以拓展到的位置)入栈;
  18. 更新最后一次出栈的栈顶元素其对应的值;
  19. }
  20. }
  21. 输出结果;

 

PS:将破坏栈单调性的元素都出栈后,最后一次出栈的元素就是当前入栈元素能拓展到的最左位置,更新其对应的值,并将其位置入栈。

 

 

应用

以上就是一个单调栈的定义及其实现,下面就来说一下它可以解决哪些问题。其实我也不能给出证明,以证明它为什么能完成这些功能,只是简单的把它的用途说一下,碰到问题时就需要大家灵活运用了。

 

1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。

2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。

3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

 

 

对应题目:下面先只给出对应的题目和大体思路,至于题解稍后完成后再一一补充。当然这只是现阶段我自己碰到过的,可能不全,还请大家多多补充指正。

 

应用1对应题目:

1.POJ 3250

题意:有一群牛站成一排,每头牛都是面朝右的,每头牛可以看到他右边身高比他小的牛。给出每头牛的身高,要求每头牛能看到的牛的总数。

思路:这也就是应用1所说的求每个数和它右边第一个比它大的数之间的数的个数,分别求出后相加即可。朴素的做法是双重循环遍历,时间复杂度为O(n^2),用单调栈为O(n)。

题解POJ 3250题解。

 

 

应用2对应题目:

1.POJ 2559

题意:有N个矩形,宽度都为1,给出N个矩形的高度,求由这N个矩形组成的图形包含的最大的矩形面积。

思路:可以转化为求区间最小值乘以区间长度的最大值。普通的思路是两重循环遍历,时间复杂度为O(n^2),用单调栈为O(n)。

题解POJ 2559 题解。

2.POJ 3494

题意:求仅由0,1组成的矩阵中,全部由1组成的小矩阵的最大面积。

思路:这个是上一题POJ 2559的升级版,把一维的操作变成二维的即可。这之前需要一个预处理。

题解POJ 3494题解。

 

 

应用3对应题目:

1.POJ 2796

题意:给出一个序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大。

思路:直接用单调栈解决即可,由于维护单调栈的过程中会改变原数组的值,所以需要加一个sum数组保存前缀和,也方便计算区间元素和。

题解POJ 2796题解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值