什么时候用单调栈?
通常是一维数组,寻找元素的右边或左边第一个比自己大或小的元素的位置
单调栈的原理
本质是空间换时间,用一个栈记录遍历过的元素
单调栈存放的元素是元素的下标i
如果求一个元素右边第一个更大元素,单调栈就是递增(从栈头到栈底)的,如果求一个元素右边第一个更小元素,单调栈就是递减的。
题目
1. 每日温度
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
思路:
(1)单调递增栈考虑三种情况:
a. 当前元素小于栈顶元素:递增栈(栈头到栈底),入栈
b. 当前元素等于栈顶元素:入栈
c. 当前元素大于栈顶元素:不满足递增栈,出栈
(2)初始化:记录位置结果的res初始化为0,单调栈初始化为只含有第一个元素0
代码:
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
stack=[0] #用来记录下标,单调递增栈
res=[0]*len(temperatures)
for i in range(1,len(temperatures)):
#如果小于等于栈顶元素,入栈
if stack and temperatures[i]<=temperatures[stack[-1]]:
stack.append(i)
else: #如果大于栈顶元素,出栈
while stack and temperatures[i]>temperatures[stack[-1]]:
top=stack.pop()
res[top]=i-top
stack.append(i)
return res
2. 下一个更大的元素Ⅰ
nums1
中数字 x
的 下一个更大元素 是指 x
在 nums2
中对应位置 右侧 的 第一个 比 x
大的元素。
给你两个 没有重复元素 的数组 nums1
和 nums2
,下标从 0 开始计数,其中nums1
是 nums2
的子集。
对于每个 0 <= i < nums1.length
,找出满足 nums1[i] == nums2[j]
的下标 j
,并且在 nums2
确定 nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1
。
返回一个长度为 nums1.length
的数组 ans
作为答案,满足 ans[i]
是如上所述的 下一个更大元素 。
思路:
单调递增栈考虑三种情况:
a. 当前元素小于栈顶元素:满足递增栈(从栈头到栈底),入栈
b. 当前元素等于栈顶元素:题目要求右边第一个比自己大的元素,入栈
c. 当前元素大于栈顶元素:判断栈顶元素是否在nums1中出现过,(注意栈里的元素是nums2的元素),如果出现过,开始记录结果(因为当前元素在栈顶元素的右边,如果比它大,则是第一个比它大的元素),出栈
3. 下一个更大的元素Ⅱ
给定一个循环数组 nums
( nums[nums.length - 1]
的下一个元素是 nums[0]
),返回 nums
中每个元素的 下一个更大元素 。
数字 x
的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1
。
思路:
本题是循环数组,有两种方法
方法一:将两个nums数组拼接,使用单调栈计算出每一个元素的下一个最大值,最后再把结果集即result数组resize到原数组大小就可以了
方法二:不扩充nums,而是在遍历的过程中模拟走了两次nums
4. 接雨水
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
思路:
本题目的是寻找一个元素,右边最大元素以及左边最大元素,计算雨水面积,因此可以使用单调栈(从栈顶到栈底递增)栈顶和栈顶的下一个元素以及要入栈的三个元素组成了雨水面积的高度和宽度
考虑三种情况:
a. 当前元素小于栈顶元素:入栈
b. 当前元素等于栈顶元素:更新栈顶,即栈顶元素出栈,当前元素入栈
c. 当前元素大于栈顶元素:栈顶元素出栈 mid=stack.pop()
雨水高度等于新栈顶元素和当前元素高度的最小值-出栈元素的高度
即h=min(height[stack[-1], height[i])-height[mid]
雨水宽度等于右边元素下标-左边元素下标-1,即w=i-stack[-1]-1
雨水面积=雨水高度*雨水宽度 即 res+=h*w
5. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路:
本题要找每个柱子左右两边第一个小于小于该柱子的柱子,所以是单调递减栈(从栈顶到栈头),和接雨水的顺序相反。栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度
(1)考虑三种情况:
a. 当前元素大于栈顶元素:直接入栈
b. 当前元素等于栈顶元素:直接入栈
c. 当前元素小于栈顶元素:栈顶元素出栈 mid=stack.pop()
当前矩形高度:h=heights[mid]
当前矩形宽度:w=i-stack[-1]-1
矩形面积:result=max(res,w*h)
(2)注意:需要在heights数组的首尾加上0
why?末尾加上0,若数组为[2,4,6,8],入栈后单调递减,始终没有走情况三,最后输出为0
首位加上0,若数组位[8,6,4,2],8入栈后,6与8比较,8弹出,此时栈空,6入栈,4与6比较,6弹出,此时栈空,4入栈,由此重复,result为0
因此需要在heights数组前后各加一个元素0