题目难度: 中等
原题链接
今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复
剑指offer2
就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
请根据每日 气温 列表 temperatures ,重新生成一个列表,要求其对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1:
- 输入: temperatures = [73,74,75,71,69,72,76,73]
- 输出: [1,1,4,2,1,1,0,0]
示例 2:
- 输入: temperatures = [30,40,50,60]
- 输出: [1,1,1,0]
示例 3:
- 输入: temperatures = [30,60,90]
- 输出: [1,1,0]
提示:
- 1 <= temperatures.length <= 10^5
- 30 <= temperatures[i] <= 100
题目思考
- 可以使用什么数据结构模拟整个过程?
解决方案
思路
- 分析题目, 最容易想到的思路是暴力两层循环: 外层遍历每个起点, 内层遍历之后第一个更高温度, 两者下标差值就是当前位置至少等待的天数
- 但这样的时间复杂度达到了 O(N^2), 根据题目输入规模, 肯定会超时, 如何优化呢?
- 正向遍历时, 由于我们无法知道后续温度情况, 所以只能暴力, 那么我们换个思路, 采用逆序遍历, 实时统计当前后续温度分布
- 这里如果我们只维护最大温度和对应下标是不够的, 因为某个温度的后面更高温度中, 离它最近的并不一定就是整个后面最高的温度
- 所以我们需要维护一个递增的温度和其下标列表, 从而快速定位每个温度后面的第一个更高温度
- 根据需求, 我们可以采用单调栈的方式来实现它, 栈里面存储下标, 且保证从栈顶到栈底的下标和温度都是递增的
- 在逆序遍历每个温度时, 先将栈顶小于等于它的温度弹出, 原因如下:
- 当前温度更高, 且下标更小, 意味着再向前遍历时, 最近的更高温度只可能是当前温度, 而不可能再是后面的栈顶温度了
- 换句话说, 栈顶已经没用了, 所以可以安全弹出
- 然后判断栈中是否还有元素:
- 如果有, 则栈顶就是后面最近的更高温度 (否则会在上一步被弹出), 它与当前下标的差值即为需要等待的天数
- 如果没有, 则说明后续没有更高的温度, 等待天数是 0
- 最后将当前下标压入栈中, 此时栈中存储的下标和对应的温度从栈顶到栈底都是单调递增的
- 下面的代码就对应了上面的整个过程, 并且有详细的注释, 方便大家理解
复杂度
- 时间复杂度 O(N): 数组每个元素最多处理 2 遍 (压入和弹出栈)
- 空间复杂度 O(N): 栈最多存 N 个元素
代码
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
stack = []
# 初始化等待天数都是0
res = [0] * n
for i in range(n)[::-1]:
# 逆序遍历每日温度
t = temperatures[i]
while stack and temperatures[stack[-1]] <= t:
# 栈顶存在且其温度不超过当前温度, 弹出
# 因为再向前遍历时, 最近的更高温度只可能是当前温度, 而不可能再是后面的栈顶温度了
# 换句话说, 栈顶已经没用了, 所以可以安全弹出
stack.pop()
if stack:
# 如果栈中仍有元素, 则栈顶就是后面最近的更高温度 (否则会在上一步被弹出)
# 它与当前下标的差值即为需要等待的天数
res[i] = stack[-1] - i
# 如果栈中没有元素, 则说明后面没有更高温度了, 使用默认值0
# 然后将当前下标压入栈中, 此时栈中存储的下标和对应的温度从栈顶到栈底都是单调递增的
stack.append(i)
return res
大家可以在下面这些地方找到我~😊
我的 GitHub
我的 Leetcode
我的 CSDN
我的知乎专栏
我的头条号
我的牛客网博客
我的公众号: 算法精选, 欢迎大家扫码关注~😊