【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
相关推荐
用户8356290780519 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
用户8356290780519 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
你好潘先生17 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
Agent_大师18 小时前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码18 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf18 小时前
FastAPI 如何连接 MySQL
后端·python
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780511 天前
使用 Python 在 PDF 中创建与管理书签
后端·python
MeixianAgent2 天前
Python 回测数据入口怎么验?历史 K 线入库前先做 5 个检查
后端·python
咕白m6252 天前
用 Python 实现一键批量查找与替换 Excel 数据
后端·python