• 算法专题-单调栈


    定义

    一句话解释单调栈,就是一个栈,里面的元素的大小按照他们所在栈内的位置,满足一定的单调性。

    单调栈(Monotone Stack):一种特殊的栈。在栈的「先进后出」规则基础上,要求「从 栈顶 到 栈底 的元素是单调递增(或者单调递减)」。其中满足从栈顶到栈底的元素是单调递增的栈,叫做「单调递增栈」。满足从栈顶到栈底的元素是单调递减的栈,叫做「单调递减栈」。

    注意:这里定义的顺序是从「栈顶」到「栈底」。有的文章里是反过来的。本文全文以「栈顶」到「栈底」的顺序为基准来描述单调栈。

    单调递增栈

    单调递增栈:只有比栈顶元素小的元素才能直接进栈,否则需要先将栈中比当前元素小的元素出栈,再将当前元素入栈。

    这样就保证了:栈中保留的都是比当前入栈元素大的值,并且从栈顶到栈底的元素值是单调递增的。

    单调递增栈的入栈、出栈过程如下:

    • 假设当前进栈元素为 x,如果 x 比栈顶元素小,则直接入栈。

    • 否则从栈顶开始遍历栈中元素,把小于 x 或者等于 x 的元素弹出栈,直到遇到一个大于 x 的元素为止,然后再把 x 压入栈中。

    下面我们以数组 [2, 7, 5, 4, 6, 3, 4, 2] 为例,模拟一下「单调递增栈」的进栈、出栈过程。具体过程如下:
    在这里插入图片描述
    最终栈中元素为 [7, 6, 4, 2]。因为从栈顶(右端)到栈底(左侧)元素的顺序为 2, 4, 6, 7,满足递增关系,所以这是一个单调递增栈。

    我们以上述过程第 5 步为例,所对应的图示过程为:

    在这里插入图片描述

    单调递减栈

    单调递减栈:只有比栈顶元素大的元素才能直接进栈,否则需要先将栈中比当前元素大的元素出栈,再将当前元素入栈。

    这样就保证了:栈中保留的都是比当前入栈元素小的值,并且从栈顶到栈底的元素值是单调递减的。

    单调递减栈的入栈、出栈过程如下:

    • 假设当前进栈元素为 x,如果 x 比栈顶元素大,则直接入栈。

    • 否则从栈顶开始遍历栈中元素,把大于 x 或者等于 x 的元素弹出栈,直到遇到一个小于 x 的元素为止,然后再把 x 压入栈中。

    下面我们以数组 [4, 3, 2, 5, 7, 4, 6, 8] 为例,模拟一下「单调递减栈」的进栈、出栈过程。具体过程如下:

    数组元素:[4, 3, 2, 5, 7, 4, 6, 8],遍历顺序为从左到右。
    在这里插入图片描述
    最终栈中元素为 [2, 4, 6, 8]。因为从栈顶(右端)到栈底(左侧)元素的顺序为 8, 6, 4, 2,满足递减关系,所以这是一个单调递减栈。

    我们以上述过程第 6 步为例,所对应的图示过程为:
    在这里插入图片描述

    单调栈适用场景

    单调栈可以在时间复杂度为 O ( n ) O(n) O(n) 的情况下,求解出某个元素左边或者右边第一个比它大或者小的元素。

    所以单调栈一般用于解决一下几种问题:

    • 寻找左侧第一个比当前元素大的元素。

    • 寻找左侧第一个比当前元素小的元素。

    • 寻找右侧第一个比当前元素大的元素。

    • 寻找右侧第一个比当前元素小的元素。

    下面分别说一下这几种问题的求解方法。

    寻找左侧第一个比当前元素大的元素

    从左到右遍历元素,构造单调递增栈(从栈顶到栈底递增):

    • 一个元素左侧第一个比它大的元素就是将其「插入单调递增栈」时的栈顶元素。

    • 如果插入时的栈为空,则说明左侧不存在比当前元素大的元素。

    寻找左侧第一个比当前元素小的元素

    从左到右遍历元素,构造单调递减栈(从栈顶到栈底递减):

    • 一个元素左侧第一个比它小的元素就是将其「插入单调递减栈」时的栈顶元素。

    • 如果插入时的栈为空,则说明左侧不存在比当前元素小的元素。

    寻找右侧第一个比当前元素大的元素

    从左到右遍历元素,构造单调递增栈(从栈顶到栈底递增):

    • 一个元素右侧第一个比它大的元素就是将其「弹出单调递增栈」时即将插入的元素。

    • 如果该元素没有被弹出栈,则说明右侧不存在比当前元素大的元素。

    从右到左遍历元素,构造单调递增栈(从栈顶到栈底递增):

    • 一个元素右侧第一个比它大的元素就是将其「插入单调递增栈」时的栈顶元素。

    • 如果插入时的栈为空,则说明右侧不存在比当前元素大的元素。

    寻找右侧第一个比当前元素小的元素

    从左到右遍历元素,构造单调递减栈(从栈顶到栈底递减):

    • 一个元素右侧第一个比它小的元素就是将其「弹出单调递减栈」时即将插入的元素。

    • 如果该元素没有被弹出栈,则说明右侧不存在比当前元素小的元素。

    从右到左遍历元素,构造单调递减栈(从栈顶到栈底递减):

    • 一个元素右侧第一个比它小的元素就是将其「插入单调递减栈」时的栈顶元素。

    • 如果插入时的栈为空,则说明右侧不存在比当前元素小的元素。

    上边的分类解法有点绕口,可以简单记为以下条规则:

    无论哪种题型,都建议从左到右遍历元素。

    • 查找 「比当前元素大的元素」 就用 单调递增栈,查找 「比当前元素小的元素」 就用 单调递减栈。

    • 从 「左侧」 查找就看 「插入栈」 时的栈顶元素,从 「右侧」 查找就看 「弹出栈」 时即将插入的元素。

  • 相关阅读:
    Linux-VI和VIM
    图解辗转相除法求解最大公约数
    英语日常单词、短语、语句
    5G+教育,带来全新的教育模式
    Python 星号的妙用 —— 灵活的序列解包
    采购和合同管理
    Python中的pop()方法:删除和提取列表元素的利器
    java基础之什么是面向对象?[11]
    软考 系统架构设计师系列知识点之软件架构风格(5)
    传播问卷调查数据不够?自己生成假数据!
  • 原文地址:https://blog.csdn.net/weixin_43892514/article/details/133825513