代码随想录单调栈|739. 每日温度、496.下一个更大元素 I、503.下一个更大元素II、42. 接雨水、84.柱状图中最大的矩形
-
- [739. 每日温度](#739. 每日温度)
- [496.下一个更大元素 I](#496.下一个更大元素 I)
- 503.下一个更大元素II
- [42. 接雨水 -- 面试常考题](#42. 接雨水 -- 面试常考题)
- 84.柱状图中最大的矩形
739. 每日温度
思路
:
找到这个元素后面 第一个比这个元素大的元素,算它们的距离(下标之差)
暴力解法:时间复杂度n2
单调栈:时间复杂度n2
单调栈适合:求当前元素左边(右边)比当前元素大(小)的元素
单调栈中存放的是下标,存放的元素是递增(从栈顶到栈底)还是递减呢?递增(第一个比它大的元素),递减(第一个比它小的元素)
单调栈的作用: 记录遍历过的元素!!
python代码
:
python
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer = [0]*len(temperatures)
stack = [0] # 存放下标
for i in range(1, len(temperatures)):
if temperatures[i] <= temperatures[stack[-1]]:
stack.append(i)
else:
while len(stack) != 0 and temperatures[i] > temperatures[stack[-1]]:
answer[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return answer
496.下一个更大元素 I
思路
:
本题本质还是对nums2采用单调栈的算法计算下一个最大元素,但是返回的result需要和nums1大小一致,顺序一致,因此对nums2中的元素计算完后需要映射回nums1,除此之外,本题的result数组应该初始化为-1
两个没有重复元素 的数组 nums1 和 nums2,采用map进行映射,使用哈希表数据结构,但是对于python实现,可以使用index函数
python代码
:
python index函数
nums1 = [1, 3, 5, 7]
nums1.index(5) 输出: 2
python
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
result = [-1]*len(nums1)
stack = [0]
for i in range(1, len(nums2)):
if nums2[i] <= nums2[stack[-1]]:
stack.append(i)
else:
while len(stack)!= 0 and nums2[i] > nums2[stack[-1]]:
if nums2[stack[-1]] in nums1: # 这里注意不要写反了,nums2[stack[-1]]和nums2[i]
index = nums1.index(nums2[stack[-1]])
result[index] = nums2[i]
stack.pop()
stack.append(i)
return result
503.下一个更大元素II
思路
:
本题相比前面两题,变为循环数组了(数组可以首尾相连了)
思路1: 扩容,两个数组拼在一起,然后单调栈
例如:nums = [1,2,1],扩容后:nums = [1,2,1,1,2,1]
result = [2,-1,2,2,-1,_] 取前3个即:[2,-1,2]
思路2: 用取模的方式模拟转圈的过程 (设计成环都可以这么操作)
for i in range(len(nums)*2):
i = i%len(nums)
遍历时对数组长度×2,然后实际再进行取模,所以在超出数组长度的时候,i又回来了
python代码
:
n = [1, 2, 3]
n*2 输出:[1, 2, 3, 1, 2, 3]
n+n 输出:[1, 2, 3, 1, 2, 3]
python
# 思路1
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
n = len(nums)
nums = nums*2
result = [-1]*len(nums)
stack = [0]
for i in range(1, len(nums)):
if nums[i] <= nums[stack[-1]]:
stack.append(i)
else:
while len(stack) != 0 and nums[i] > nums[stack[-1]]:
result[stack[-1]] = nums[i]
stack.pop()
stack.append(i)
res = result[:n]
return res
python
# 思路2
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
result = [-1]*len(nums)
stack = [0]
for i in range(1, len(nums)*2):
i = i%len(nums)
if nums[i] <= nums[stack[-1]]:
stack.append(i)
else:
while len(stack) != 0 and nums[i] > nums[stack[-1]]:
result[stack[-1]] = nums[i]
stack.pop()
stack.append(i)
return result
42. 接雨水 -- 面试常考题
思路
:
思路1: 暴力解法,复杂度n2
两层for循环,外层for循环一个遍历柱子,里层for循环 去找每个柱子右边比它高的第一个柱子高度及左边比它高的第一个柱子的高度是多少
思路2: 双指针优化,复杂度O(n)
提前预处理两个数组
思路3: 单调栈
这里可以借助单调栈的特性,栈内的元素是单调递增的,因此在获得了某一个元素的右边第一个比它大的元素,那么栈里的下一个元素就是左边第一个比它大的元素
栈中还是存放下标
python代码
:
python
class Solution:
def trap(self, height: List[int]) -> int:
result= 0
stack= [0]
for i in range(1, len(height)):
if height[i] <= height[stack[-1]]:
stack.append(i)
else:
while stack and height[i] > height[stack[-1]]:
mid = stack.pop()
if stack:
w = i - stack[-1] - 1
h = min(height[i], height[stack[-1]]) - height[mid] # 第一次提交把这里重命名为height了,调试了半天。。
result += w*h
stack.append(i)
return result
84.柱状图中最大的矩形
思路
:
和接雨水题目遥相呼应,一个是求柱子外面的,一个是求柱子里面的
-
思路1: 暴力解法,复杂度n2
两次for循环
以某个柱子为中心,找左边比它矮的,右边比它矮的,确定高,然后根据距离确定宽
-
思路2: 双指针法
-
思路3: 单调栈
此处的单调栈是单调递减的,因为要找左边和右边第一个比它小的,中间就是大于等于它的,高度就是当前柱子的高度,宽度为中间柱子的宽度;
首位加0,(尾部加0)避免[2,4,6,8]这样单调递增的数组;(头部加0)对于[8,6,4,2]若满足当前元素大于栈顶元素,弹出后,栈为空,则不会进行计算面积,避免栈为空的情况
python代码
:
python
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights = [0] + heights + [0] # 或者 heights.insert(0, 0) heights.append(0)
result = 0
stack = [0]
for i in range(1, len(heights)):
if heights[i] >= heights[stack[-1]]:
stack.append(i)
else:
while stack and heights[i] < heights[stack[-1]]:
mid = stack.pop()
if stack:
w = i - stack[-1] - 1
h = heights[mid]
result = max(result, w*h)
stack.append(i) # 第一次忘记了这行代码
return result