【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
相关推荐
“AI国潮设计-小江”13 分钟前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
wenwen_chao35 分钟前
Python学习与开发资源合集:实用技巧与工具推荐
python
for_ever_love__2 小时前
函数——参数、lambda、*args与作用域
python·函数·大模型开发·编程基础
东莞市云毅网络有限公司2 小时前
企业资料时效性判定:Last-Modified、正文日期与版本字段三路交叉
python·sqlite·自动化运维·geo·数据监测
Query*2 小时前
深入浅出LangGraph【一】_基础篇
python·ai·langchain
XiaoMaqqqq2 小时前
市面上正规的IP驱动产业新场景新工具哪家强
运维·python·网络协议·tcp/ip
ShyanZh3 小时前
【Python3基础】19-Socket 与 TCP、UDP 编程
python·网络协议·tcp/ip·udp
惊讶的猫3 小时前
工具选对之后还不够:商城 Agent 的参数与权限治理
开发语言·python
天赐范式4 小时前
天赐范式第186天:让方差开始自洽——Bulmer效应的闭式验证
python·数字生命·天赐范式·动态运行时·遗传方差·bulmer效应
泡茶喝茶写代码4 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据