【Leetcode】单调栈

单调栈

单调栈是一种高效的栈结构,常用来解决数组中元素顺序相关的问题,如"下一个更大元素"等。其核心思想是通过维护栈内元素的单调性,并记录元素的间顺序关系,以减少不必要的比较操作。通常情况下,由于每个元素入栈和出栈各一次,时间复杂度为 O(n)。

在使用过程中,需要关注两个重点,分别是栈顶元素的弹出条件 和预期目标的结果收集,具体而言:

  • 弹出条件:满足什么条件时栈顶元素弹出。这往往取决于何时可以得到目标结果,这也可以用于判断应该使用单调增栈还是单调减栈。
  • 结果收集:预期的目标结果如何收集。对于简单的问题,目标结果往往可以直接得到,而对于稍复杂的问题,如接雨水等,则需要进行一定的计算和转换。

如何确定这两点呢?我们将关注点放在栈顶,当有元素入栈时,判断是否可以找到栈顶元素的目标结果,如果找到了,就满足触发条件,接着就可以收集目标结果并弹出栈顶元素。

另外,在单调栈的使用时,通常使用元素的索引进行栈操作,这是因为索引中附带了元素的值和位置信息,以便进行较复杂的操作。

下面从一些经典例题出发,由浅入深地介绍单调栈的使用。

例题 1:下一个更大元素(基础)

问题描述: 在一个没有重复值的数组中,找到每个元素右侧第一个比它大的元素。

基本思路:将数组元素依次压入单调栈中,判断入栈元素是否比栈顶元素大,当比栈顶元素大时,即找到了栈顶元素的下一个更大元素,栈顶元素弹出并收集目标结果。

  • 触发条件:当入栈元素大于栈顶元素时,找到了栈顶元素的下一个更大元素,栈顶元素弹出。
  • 结果收集:栈顶元素弹出时,收集待入栈元素,即为栈顶元素的下一个更大元素。
练习 1.1:496. 下一个更大元素 I - 力扣(LeetCode)(简单)
Python 复制代码
# 496. 下一个更大元素 I
# https://leetcode.cn/problems/next-greater-element-i/description/
def nextGreaterElement(nums1: List[int], nums2: List[int]) -> List[int]:
    # 单调栈
    # 将nums2转化为字典,通过单调栈找到下一个更大元素
    st = []
    ans = {n:-1 for n in nums2}
    for n in nums2:
        while st and n > st[-1]:
            ans[st.pop()] = n
        st.append(n)
    
    return [ans[n] for n in nums1]
练习 1.2:503. 下一个更大元素 II - 力扣(LeetCode)(简单)
Python 复制代码
# 503. 下一个更大元素 II
# https://leetcode.cn/problems/next-greater-element-ii/description/
def nextGreaterElements(nums: List[int]) -> List[int]:
    # 单调栈,
    # 索引操作,通过取余实现循环索引
    n = len(nums)
    ans = [-1]*n
    st = []
    for i in range(2*n):
        idx = i%n
        while st and nums[idx] > nums[st[-1]]:
            ans[st.pop()] = nums[idx]
        st.append(idx)
    return ans
练习 1.3:1944. 队列中可以看到的人数 - 力扣(LeetCode)(中等)

问题描述:返回队列中每个人在他右侧能看到的人数

基本思路 :当前位置可以观察到的最远位置是下一个更大值 ,但由于存在遮挡问题(如 5 夹在 8 和 11 之间),观察时不会被看到。因此,按照下一个更大值的思路,并不能剔除被遮挡的元素,因此按照下一个更大值的做法行不通。

转换一下思路,从当前位置往左可以被哪些位置看到呢------上一个更小值,此时只需要确保从当前位置往左依次递减,当遇到栈顶的更大值时,栈顶元素弹出,就可以确保元素被正确统计。需要注意,这里的栈顶元素是被看到的元素,而入栈元素是需要统计的。

  • 触发条件:当入栈元素大于栈顶元素时,非单调递减,即找到了当前元素可看到的一个元素,栈顶元素弹出。
  • 结果收集:栈顶元素代表入栈元素可以看到的元素,当栈顶元素弹出时,将入栈元素可看到的人数加 1。另外需要注意的是,当栈不为空时,栈顶元素也可以被当前元素看到,需要再加 1。
Python 复制代码
# 1944. 队列中可以看到的人数
# https://leetcode.cn/problems/number-of-visible-people-in-a-queue/description/
def canSeePersonsCount(heights: List[int]) -> List[int]:
    # 单调栈,从末端开始的单调递减栈
    # i位置能看到的是右侧单调增且不被遮挡的元素数量
    n = len(heights)
    st = []
    ans = [0]*n
    for i in range(n-1,-1,-1):
        while st and heights[i] > heights[st[-1]]:
            st.pop()
            ans[i] += 1 # 统计入栈元素可看到的人数
        # 栈不为空时,栈顶元素也可以被看到
        ans[i] += 1 if st else 0
        st.append(i)
    return ans
相关推荐
冷雨夜中漫步3 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
郝学胜-神的一滴3 小时前
深入解析Python字典的继承关系:从abc模块看设计之美
网络·数据结构·python·程序人生
百锦再3 小时前
Reactive编程入门:Project Reactor 深度指南
前端·javascript·python·react.js·django·前端框架·reactjs
喵手5 小时前
Python爬虫实战:旅游数据采集实战 - 携程&去哪儿酒店机票价格监控完整方案(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集结果csv导出·旅游数据采集·携程/去哪儿酒店机票价格监控
2501_944934735 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
helloworldandy5 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
肖永威7 小时前
macOS环境安装/卸载python实践笔记
笔记·python·macos
TechWJ7 小时前
PyPTO编程范式深度解读:让NPU开发像写Python一样简单
开发语言·python·cann·pypto
枷锁—sha7 小时前
【SRC】SQL注入WAF 绕过应对策略(二)
网络·数据库·python·sql·安全·网络安全
abluckyboy7 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法