深入理解单调栈及其应用:以LeetCode-Py项目为例

深入理解单调栈及其应用:以LeetCode-Py项目为例

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

1. 单调栈基础概念

单调栈是一种特殊的栈结构,它在普通栈的"后进先出"特性基础上,增加了元素单调性的约束。根据单调性的不同,单调栈可以分为两种主要类型:

1.1 单调递增栈

单调递增栈要求从栈顶到栈底的元素保持递增顺序。这种栈的特点是:

  • 新元素入栈前,会移除栈中所有比它小的元素
  • 栈顶元素始终是当前栈中最小的元素
  • 适合解决"寻找左侧/右侧第一个更大元素"的问题

操作流程

  1. 当新元素小于栈顶元素时,直接入栈
  2. 当新元素大于等于栈顶元素时,弹出栈顶元素直到满足条件1
  3. 将新元素压入栈中

1.2 单调递减栈

单调递减栈要求从栈顶到栈底的元素保持递减顺序。这种栈的特点是:

  • 新元素入栈前,会移除栈中所有比它大的元素
  • 栈顶元素始终是当前栈中最大的元素
  • 适合解决"寻找左侧/右侧第一个更小元素"的问题

操作流程

  1. 当新元素大于栈顶元素时,直接入栈
  2. 当新元素小于等于栈顶元素时,弹出栈顶元素直到满足条件1
  3. 将新元素压入栈中

2. 单调栈的核心应用场景

单调栈主要用于解决一类特定的问题:在序列中快速找到每个元素左右两侧第一个满足某种条件的元素。具体来说,它可以高效解决以下四类问题:

2.1 寻找左侧第一个更大元素

使用单调递增栈,从左向右遍历:

  • 入栈时栈顶元素即为左侧第一个更大的元素
  • 栈为空表示左侧没有更大的元素

2.2 寻找左侧第一个更小元素

使用单调递减栈,从左向右遍历:

  • 入栈时栈顶元素即为左侧第一个更小的元素
  • 栈为空表示左侧没有更小的元素

2.3 寻找右侧第一个更大元素

有两种实现方式:

  1. 使用单调递增栈,从左向右遍历:

    • 元素被弹出时,导致它弹出的元素就是右侧第一个更大的元素
    • 未被弹出的元素表示右侧没有更大的元素
  2. 使用单调递增栈,从右向左遍历:

    • 入栈时栈顶元素即为右侧第一个更大的元素
    • 栈为空表示右侧没有更大的元素

2.4 寻找右侧第一个更小元素

同样有两种实现方式:

  1. 使用单调递减栈,从左向右遍历:

    • 元素被弹出时,导致它弹出的元素就是右侧第一个更小的元素
    • 未被弹出的元素表示右侧没有更小的元素
  2. 使用单调递减栈,从右向左遍历:

    • 入栈时栈顶元素即为右侧第一个更小的元素
    • 栈为空表示右侧没有更小的元素

3. 单调栈的通用模板

3.1 单调递增栈模板

def increasing_stack(nums):
    stack = []
    for num in nums:
        # 维护栈的单调性:弹出所有小于当前元素的栈顶元素
        while stack and num > stack[-1]:
            stack.pop()
        stack.append(num)

3.2 单调递减栈模板

def decreasing_stack(nums):
    stack = []
    for num in nums:
        # 维护栈的单调性:弹出所有大于当前元素的栈顶元素
        while stack and num < stack[-1]:
            stack.pop()
        stack.append(num)

4. 经典问题解析

4.1 下一个更大元素问题

问题描述:给定两个数组nums1和nums2,其中nums1是nums2的子集。对于nums1中的每个元素,找到它在nums2中对应位置右侧第一个更大的元素。

解题思路

  1. 使用单调递增栈预处理nums2,建立元素与其下一个更大元素的映射
  2. 遍历nums1,直接从映射表中查询结果

关键点

  • 单调栈帮助我们以O(n)时间复杂度建立映射关系
  • 哈希表存储中间结果,实现快速查询

4.2 每日温度问题

问题描述:对于每天的温度,计算需要等待多少天才能遇到更高的温度。

解题思路

  1. 使用单调递减栈存储日期索引
  2. 当遇到更高温度时,计算日期差并更新结果数组
  3. 栈中剩余元素表示没有更高温度的日子

优化点

  • 直接存储索引而非温度值,便于计算天数差
  • 结果数组初始化为0,无需处理栈中剩余元素

5. 算法复杂度分析

单调栈算法的时间复杂度通常为O(n),其中n是输入数组的长度。这是因为:

  • 每个元素最多入栈一次、出栈一次
  • 虽然存在嵌套循环,但内层while循环的总操作次数不会超过n次

空间复杂度一般为O(n),主要用于存储栈结构和结果。

6. 实际应用中的注意事项

  1. 元素相等处理:根据问题需求决定是否保留相等元素
  2. 边界条件:空输入、所有元素相同等特殊情况
  3. 索引与值:根据问题选择存储值还是索引
  4. 遍历方向:从左到右或从右到左会影响求解逻辑

单调栈是一种强大而高效的数据结构,特别适合处理"邻近更大/更小元素"类问题。通过LeetCode-Py项目中的这些经典问题,我们可以深入理解其工作原理和应用技巧。掌握单调栈不仅能够提升算法解题能力,也能帮助我们更好地处理实际开发中的类似问题。

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魏秦任

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

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

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

打赏作者

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

抵扣说明:

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

余额充值