动态规划结束,开始学单调栈
单调栈是什么?
定义
单调栈是一种特殊的栈结构,其元素按照单调递增或单调递减的顺序排列。它主要用于解决需要维护单调性的问题,例如寻找下一个更大或更小的元素。
工作原理
-
单调递增栈:栈内元素从栈底到栈顶递增。当新元素小于栈顶元素时,栈顶元素被弹出,直到新元素可以保持单调性。
-
单调递减栈:栈内元素从栈底到栈顶递减。当新元素大于栈顶元素时,栈顶元素被弹出,直到新元素可以保持单调性。
739. 每日温度
思路
这道题标准的单调栈应用 ,核心思路:用单调递减栈存储数组下标,遍历温度时,找到每个元素右侧第一个更大的元素,计算天数差。
-
初始化
-
answer:长度和温度数组一致,默认值 0(无更高温度时直接用) -
stack:空栈,专门存下标,不存温度值
-
-
遍历逻辑
-
循环条件:栈不为空 且 当前温度 > 栈顶下标对应的温度
-
弹出栈顶元素,计算
当前下标 - 弹出下标→ 就是下一个更高温度的天数 -
把当前下标压入栈,保持栈的单调递减特性
-
-
时间 / 空间复杂度
-
时间:O(n),每个元素入栈、出栈各一次
-
空间:O(n),栈最多存储全部元素
-
提交
python
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
answer = [0] * n # 初始化结果数组,默认全0
stack = [] # 单调栈:存储下标,对应温度递减
for i in range(n):
# 当前温度 > 栈顶温度,说明找到下一个更高温度
while stack and temperatures[i] > temperatures[stack[-1]]:
prev_idx = stack.pop() # 弹出栈顶下标
answer[prev_idx] = i - prev_idx # 计算天数差
# 当前下标入栈,维持单调递减
stack.append(i)
return answer
496.下一个更大元素 I
题目链接496. 下一个更大元素 I - 力扣(LeetCode)
题干描述的第一句话也太抽象了,直接看后面和给的示例
思路
第一步:处理 nums2,用单调栈生成「下一个更大元素表」
-
栈保持递减
-
遇到比栈顶大的数:
-
栈顶弹出
-
记录:
弹出的数的下一个更大 = 当前数
-
-
最后栈里剩下的数:没有更大元素 → -1
第二步:处理 nums1
- 直接遍历每个数,去哈希表里拿结果
我刚开始想的是在nums2做一个下标和这个元素下一个更大值的映射关系
比如【1,3,4,2】映射【3,4,-1-1】,然后遍历nums1,但是这里弄下标和元素值把自己弄晕了
然后上面思路那个方法就比较清楚,直接将元素值和下一个更大元素进行映射
跳过了下标这个比较容易晕的东西
提交
python
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 1. 哈希表:key = 元素值,value = 下一个更大元素
next_greater = {}
stack = [] # 单调递减栈,存元素值
# 遍历 nums2,求每个元素的下一个更大元素
for num in nums2:
# 当前数 > 栈顶,说明栈顶找到了下一个更大元素
while stack and num > stack[-1]:
val = stack.pop()
next_greater[val] = num
# 当前数入栈,维持递减
stack.append(num)
# 栈里剩下的元素,没有更大值,设为 -1
for num in stack:
next_greater[num] = -1
# 2. 遍历 nums1,直接查表
return [next_greater[x] for x in nums1]
503.下一个更大元素II
题目链接 503. 下一个更大元素 II - 力扣(LeetCode)
这个题和我上一个
"
我刚开始想的是在nums2做一个下标和这个元素下一个更大值的映射关系
比如【1,3,4,2】映射【3,4,-1-1】,然后遍历nums1,但是这里弄下标和元素值把自己弄晕了
"
有点像,就是第一步,还是得学会这个
但是还有一个重要的事,要循环搜索
思路
1.循环数组处理技巧
数组是环形的,最后一个元素后面接着第一个元素。
不用真的拼接数组,直接遍历 2n 次,通过 i % n 映射回原数组下标,等价于把数组复制一遍接在后面。
2.单调栈核心
维护单调递减栈,栈中存原数组下标:
遍历到当前位置,若当前元素 > 栈顶下标对应的元素,说明栈顶元素找到了下一个更大元素;
弹出栈顶下标,给结果数组赋值;
持续弹出直到栈不满足递减;
只在前 n 次遍历把下标入栈,防止重复入栈。
3.初始默认值
结果数组初始全为 -1,没找到更大元素就保持 -1。
提交
python
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
n = len(nums)
res = [-1] * n
stack = [] # 单调栈:存放数组下标
# 遍历两倍长度,模拟循环数组
for i in range(2 * n):
idx = i % n # 映射回原数组下标
# 维持单调递减,找下一个更大元素
while stack and nums[idx] > nums[stack[-1]]:
top_idx = stack.pop()
res[top_idx] = nums[idx]
# 只在第一轮存入下标,避免重复
if i < n:
stack.append(idx)
return res