【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
相关推荐
Alfred king2 小时前
面试150 生命游戏
leetcode·游戏·面试·数组
思则变2 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
漫谈网络3 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
try2find4 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取5 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector7 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习7 小时前
Python入门Day2
开发语言·python
Vertira7 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉7 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗7 小时前
黑马python(二十四)
开发语言·python