代码随想录算法训练营Day41 | Leetcode739. 每日温度、496.下一个更大元素 I、503.下一个更大元素II
一、每日温度
相关题目:Leetcode739
文档讲解:Leetcode739
视频讲解:Leetcode739
1. Leetcode739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
提示:
- 1 <= temperatures.length <= 105
- 30 <= temperatures[i] <= 100
-
思路:
- 通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时可以考虑用单调栈。
- 单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。单调栈里只需要存放元素的下标 i 就可以了,如果需要使用对应的元素,直接 T[i] 就可以获取。
- 本题可以使用单调栈求解,使用递增循序(指从栈头到栈底的顺序),因为只有递增的时候,栈里要加入一个元素 i 的时候,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是 i。即:如果求一个元素右边第一个更大元素,单调栈就是递增的,如果求一个元素右边第一个更小元素,单调栈就是递减的。
- 使用单调栈主要有三个判断条件:
- 当前遍历的元素 T[i] 小于栈顶元素 T[st.top()] 的情况:将元素 T[i] 加入栈 ;
- 当前遍历的元素 T[i] 等于栈顶元素 T[st.top()] 的情况:依然要将元素 T[i] 加入栈,因为要求的是右面第一个大于本元素的位置,而不是大于等于!
- 当前遍历的元素 T[i] 大于栈顶元素 T[st.top()] 的情况:将 T[st.top()] 弹出,将元素 T[i] 加入栈,此时 result 数组可以记录:result[st.top()] = i,即 T[st.top()] 右面第一个比其大的元素是 T[i]。若弹出栈顶元素后 T[i] 仍然大于 T[st.top()] ,则重复上述操作直至单调栈时递增的或为空。
-
单调栈
###未精简版本
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
###精简版本
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer = [0]*len(temperatures)
stack = []
for i in range(len(temperatures)):
while len(stack)>0 and temperatures[i] > temperatures[stack[-1]]:
answer[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return answer
二、下一个更大元素 I
相关题目:Leetcode496
文档讲解:Leetcode496
视频讲解:Leetcode496
1. Leetcode496.下一个更大元素 I
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] 是如上所述的 下一个更大元素 。
提示:
- 1 <= nums1.length <= nums2.length <= 1000
- 0 <= nums1[i], nums2[i] <= 104
- nums1和nums2中所有整数 互不相同
- nums1 中的所有整数同样出现在 nums2 中
-
思路:
- 从题目可以看出最后是要求 nums1 的每个元素在 nums2 中下一个比当前元素大的元素,那么就要定义一个和 nums1 一样大小的数组 result 来存放结果。而题目说如果不存在对应位置就输出 -1 ,所以 result 数组如果某位置没有被赋值则应初始化为 -1。
- 在遍历 nums2 的过程中,我们要判断 nums2[i] 是否在 nums1 中出现过,因为最后是要根据 nums1 元素的下标来更新 result 数组,可以利用 index() 根据数值快速找到下标。
- 单调栈里的元素为递增顺序,只有保持递增,才能找到右边第一个比自己大的元素。而使用单调栈需要分析如下三种情况:
- 当前遍历的元素 T[i] 小于栈顶元素 T[st.top()] 的情况:此时满足递增栈(栈头到栈底的顺序),所以直接入栈。
- 当前遍历的元素 T[i] 等于栈顶元素 T[st.top()] 的情况:如果相等的话,依然直接入栈,因为我们要求的是右边第一个比自己大的元素,而不是大于等于!
- 当前遍历的元素 T[i] 大于栈顶元素 T[st.top()] 的情况:此时如果入栈就不满足递增栈了,这也是找到右边第一个比自己大的元素的时候,判断栈顶元素是否在 nums1 里出现过(注意栈里的元素是 nums2 的元素),如果出现过则开始记录结果。
-
单调栈
### 版本一
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:
index = nums1.index(nums2[stack[-1]])
result[index]=nums2[i]
stack.pop()
stack.append(i)
return result
### 版本二
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
stack = []
# 创建答案数组
ans = [-1] * len(nums1)
for i in range(len(nums2)):
while len(stack) > 0 and nums2[i] > nums2[stack[-1]]:
# 判断 num1 是否有 nums2[stack[-1]]。如果没有这个判断会出现指针异常
if nums2[stack[-1]] in nums1:
# 锁定 num1 检索的 index
index = nums1.index(nums2[stack[-1]])
# 更新答案数组
ans[index] = nums2[i]
# 弹出小元素
# 这个代码一定要放在 if 外面。否则单调栈的逻辑就不成立了
stack.pop()
stack.append(i)
return ans
三、下一个更大元素II
相关题目:Leetcode503
文档讲解:Leetcode503
视频讲解:Leetcode503
1. Leetcode503.下一个更大元素II
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。
数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。
提示:
- 1 <= nums.length <= 104
- -109 <= nums[i] <= 109
-
思路:
- 本题和 Leetcode739. 每日温度 思路基本一致,唯一区别为要循环数组。
- 方法一:将两个 nums 数组拼接在一起,使用单调栈计算出每一个元素的下一个最大值,将结果存在 result里。
- 优化:数组 result 元素初始化皆为 -1,先遍历一次数组 nums,返回每个元素的下一个最大元素对应数组 result;第二次遍历 nums 时,不将已经有下一个更大元素的数加入栈,避免重复赋值,只需对第一次遍历剩余的数再尝试寻找下一个更大元素即可,最后无论栈中有无元素无法有下一个更大元素,返回结果 result。
-
单调栈
### 版本一
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
dp = [-1] * len(nums)
stack = []
for i in range(len(nums)*2):
while(len(stack) != 0 and nums[i%len(nums)] > nums[stack[-1]]):
dp[stack[-1]] = nums[i%len(nums)]
stack.pop()
stack.append(i%len(nums))
return dp
###版本二:针对版本一的优化
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
res = [-1] * len(nums)
stack = []
#第一次遍历nums
for i, num in enumerate(nums):
while stack and num > nums[stack[-1]]:
res[stack[-1]] = num
stack.pop()
stack.append(i)
#此时stack仍有剩余,有部分数‘无下一个更大元素’待修正
#第二次遍历nums
for num in nums:
#一旦stack为空,就表明所有数都有下一个更大元素,可以返回结果
if not stack:
return res
while stack and num > nums[stack[-1]]:
res[stack[-1]] = num
stack.pop()
#不要将已经有下一个更大元素的数加入栈,这样会重复赋值,只需对第一次遍历剩余的数再尝试寻找下一个更大元素即可
#最后仍有部分最大数无法有下一个更大元素,返回结果
return res