当前位置: 首页 > news >正文

刷题总结 栈和队列:单调栈

一、原理

1.模式识别特征

数据结构:数组或字符串等有索引的顺序

问题:当数组形式的数据结构需要解决以下的索引-值配对的问题,则可以使用单调栈

①下一个更大元素 ②上一个更大元素

③下一个更小元素 ④上一个更小元素

2.栈和单调性分别体现在哪里?

单调栈的是一种用栈来记录元素用于配对最邻近的相对大小关系的方式。

问题1:栈记录了的哪些元素?

问题2:如何配对?

问题3:单调性作用?

答案1:索引-值配对中待配对的索引或待配对的值

答案2:栈内的索引与遍历到的当前元素i的值配对(正向抛出时)栈内的值与遍历到的当前元素i的索引(反向入栈时)配对

答案3:方便顺序比大小并找到配对值的开始和终止位置

以下一个更大元素为例:

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        ans = [0] * n 
        stack = []
        for i in range(n):
            while stack and temperatures[i] > temperatures[stack[-1]]:
                ch = stack.pop()
                ans[ch] = i
            # if stack: ans[i] = stack[-1]
            stack.append(i)
        return ans

for循环遍历,每遍历到一个位置i,i位置元素与栈内元素进行配对,

会遇到三种情况:

①元素i比栈内元素都小,i元素入栈

②元素i比栈内元素都大,栈清空,i元素入栈,

以上两种情况单调栈没有起到作用,单调栈的作用体现在第三种情况:

③栈内部分元素大于i,部分元素小于i:

以“中”代指元素i,“小”代指栈中比i小的元素,“大”代指栈中比i大的元素

状态:[大,小]→,中(当前元素i),

目标是找下一个大元素,栈中的小元素(的索引)与的下一个大元素就是当前元素i,即“中”,

那么单调栈抛出小元素,抛出后小元素的索引与i的值配对

单调栈用于解决索引-值配对的问题,因此,栈内元素的物理意义就是待配对的索引,

此外,反向遍历(for i in range(n - 1, -1, -1))也可以解决问题,反向遍历时,

状态:中(当前元素i),←[小,大]

单调栈抛出小,抛出后中的索引与大的值配对,即当前元素的索引与栈内大元素的值配对

3.单调栈的使用方式

(1)配对方式

配对方式有两种:

①栈内元素抛出时栈内抛出的元素的索引和栈外元素i的值配对

②栈内元素抛出后栈外元素i的索引和此时栈顶的元素的值进行配对

出现配对方式的差异的原因时栈内元素的意义不同,

当栈的单调性排列方向与遍历方向一致时:栈内抛出索引-当前元素i值

即栈内元素抛出时栈内抛出的元素的索引和栈外当前元素i的值配对

当栈的单调性排列方向与遍历方向相反时:当前元素i索引-抛出后栈顶元素值

即栈内元素抛出后栈外当前元素i的索引和此时栈顶的元素的值进行配对

(2)while抛出元素循环的不等号

无论正向与反向,

找上一个或下一个更大元素统一用单调递减栈,不等号统一是栈内stk[-1] > (栈外)当前i

找上一个或下一个更小元素统一用单调递增栈,不等号统一是栈内stk[-1] < (栈外)当前i

(3)while抛出元素的是否有等号

如果不存在重复元素,无需考虑此问题;

如果存在重复元素,则需要单独考虑当当前元素i与栈内元素相等时能否配对,

如果相等时可以匹配,则正向遍历无等号,反向有等号

如果相等时不能匹配,则正向遍历有等号,反向无等号,

此外如果是同时寻找上一个和下一个更大或更小,二者合起来写则不需要考虑等号问题,

4.复合使用单调栈

当①下一个更大元素 和 ②上一个更大元素 同时出现时,

或者当③下一个更小元素 和 ④上一个更小元素 同时出现时,

二者可写在一起,无需遍历两次

二、题单-模式识别

1.下一个更大元素:

739. 每日温度(HOT100)

496. 下一个更大元素 I(HOT100)

503. 下一个更大元素 II(HOT100)

2.同时寻找上一个和下一个更大元素:

栈内外可以相等:

42. 接雨水(HOT100)

栈内外不能相当:

654. 最大二叉树:

3.同时寻找上一个和下一个更小元素:

84. 柱状图中最大的矩形(HOT100)

三、题解

1.下一个更大元素

 下一个更大元素:

739. 每日温度

正向单调减:

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        ans = [0] * n 
        stack = []
        for i in range(n):
            while stack and temperatures[i] > temperatures[stack[-1]]:
                ch = stack.pop()
                ans[ch] = i - ch
            stack.append(i)
        return ans

反向单调减:

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        ans = [0] * n
        stack = []
        for i in range(n - 1, -1, -1):
            while stack and temperatures[i] >= temperatures[stack[-1]]:
                stack.pop()
            if stack: ans[i] = stack[-1] - i
            stack.append(i)
        return ans

496. 下一个更大元素 I

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        n = len(nums1)
        ans = [-1] * n 
        num_map = {v: k for k, v in enumerate(nums1)}
        stack = []
        for i, num in enumerate(nums2):
            while stack and num > nums2[stack[-1]]:
                ch = stack.pop()
                if nums2[ch] in num_map: ans[num_map[nums2[ch]]] = nums2[i]
            stack.append(i)
        return ans

503. 下一个更大元素 II

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        n = len(nums)
        ans = [-1] * n 
        stack = []
        for i in range(2 * n - 1, -1, -1):
            while stack and nums[i % n] >= nums[stack[-1]]:
                stack.pop()
            if stack: ans[i % n] = nums[stack[-1]]
            stack.append(i % n)
        return ans

2.同时寻找上一个和下一个更大元素:

栈内外可以相等:

42. 接雨水

(正向)

class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height)
        stk = []
        ans = 0
        for i in range(n):
            while stk and height[stk[-1]] <= height[i]:
                base = stk.pop()
                if stk:
                    h = min(height[stk[-1]], height[i]) - height[base]
                    w = i - stk[-1] - 1
                    ans += h * w 
            stk.append(i)
        return ans

栈内外不能相当:

这道题还能看出来,单调栈就是递归找最大值的迭代解法

654. 最大二叉树

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        n = len(nums)
        nodes = [TreeNode(nums[i]) for i in range(n)]
        stack = []
        for i in range(n - 1, -1, -1):
            while stack and nums[i] > nums[stack[-1]]:
                ch = stack.pop()
                nodes[i].right = nodes[ch]
            if stack: nodes[stack[-1]].left = nodes[i]
            stack.append(i)
        return nodes[stack[0]]

3.同时寻找上一个和下一个更小元素:

84. 柱状图中最大的矩形

组合写(反向):

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        left, right = [-1] * n, [n] * n
        stack = []
        for i in range(n - 1, -1, -1):
            while stack and heights[stack[-1]] >= heights[i]:
                ch = stack.pop()
                left[ch] = i
            if stack: right[i] = stack[-1]
            stack.append(i)
        return max((right[i] - left[i] - 1) * heights[i] for i in range(n))

分开写:

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        left, right = [-1] * n, [n] * n
        stack = []
        for i in range(n):
            while stack and heights[stack[-1]] >= heights[i]:
                stack.pop()
            if stack: left[i] = stack[-1]
            stack.append(i)
        for i in range(n - 1, -1, -1):
            while stack and heights[stack[-1]] >= heights[i]:
                stack.pop()
            if stack: right[i] = stack[-1]
            stack.append(i)
        return max((right[i] - left[i] - 1) * heights[i] for i in range(n))

相关文章:

  • 玩机日记 14 飞牛fnOS部署qBittorrent、AList、Jellyfin,实现下载、存取、刮削、观看一体的家庭影音中心
  • 基于 CFD 预测的机器学习第 2 部分:在 Benchmark 应用程序上使用 Stochos 预测流场
  • VMware建立linux虚拟机
  • github 部署前端静态网页(react vite)
  • 【PLL】相位检测器:PFD
  • Java多线程安全
  • QT零基础学习之路(四)--信号和槽机制
  • 第五章:队列管理模块
  • DAY40|动态规划Part08|LeetCode: 121. 买卖股票的最佳时机 、 122.买卖股票的最佳时机II 、 123.买卖股票的最佳时机III
  • 文本编辑器使用指南:Linux中的文本编辑器大冒险
  • 算法题(76):跳跃游戏II
  • 【JavaWeb13】了解ES6的核心特性,对于提高JavaScript编程效率有哪些潜在影响?
  • 静止的钉子
  • transformer架构嵌入层位置编码之动态NTK-aware位置编码
  • 第四章 哈希表
  • 每天一个Flutter开发小项目 (4) : 构建收藏地点应用 - 深入Flutter状态管理
  • 递归、搜索与回溯算法 —— 名词解析
  • Elasticsearch面试宝典【刷题系列】
  • 【深度学习神经网络学习笔记(三)】向量化编程
  • 将CUBE或3DL LUT转换为PNG图像
  • 自己能建设网站/上海谷歌seo推广公司
  • 陕西交通建设集团信息网站/如何优化网络环境
  • 中性衣服印花图案设计网站/搜索引擎推广案例
  • 网站上的文章做参考文献/万能软文范例800字
  • 网站建设一条龙/信阳seo推广
  • 网站建设学生选课课程设计报告/直播回放老卡怎么回事