题目描述
思路解析
一句话答案:LeetCode 739 每日温度的最优解是单调递减栈:栈里存还没等到更高温的那些天的下标,新来一个温度就把比它低的栈顶逐个弹出结算,等待天数就是下标差 i - j。每个下标进出栈各一次,时间 O(n)、空间 O(n),比逐天向右扫的 O(n²) 暴力快一个数量级。
每日温度这道题在问什么
给一个数组 temperatures 记录每天气温,要求对每一天算出:再过几天会出现第一个比今天更高的温度,如果之后再也没有更高温就填 0。剥掉温度的外衣,它就是经典的「下一个更大元素」问题的距离版——对每个位置找右边第一个严格更大的元素,只是答案要的不是那个值,而是两者隔了几个位置。
为什么逐天向右扫的暴力会慢
最直觉的做法是对每一天都向右线性扫描,撞到第一个更高温为止,最坏 O(n²)。慢在重复劳动:一段连续降温的日子里,每一天都要把后面同样的区间再扫一遍。
换个视角看,某一天的答案要等到「未来某天」才揭晓。与其让每一天主动向右找,不如反过来:让还没等到答案的日子排队挂起,每来一个新温度,就检查队伍里有谁的答案在此刻揭晓,统一结算。这样每天只被处理常数次,扫描的浪费就消失了。
为什么栈里的温度天然是递减的
用一个栈保存「尚未找到答案」的下标。新温度 t 到来时,只要栈顶那天的温度比 t 低,它的答案就是今天,弹出结算;重复直到栈顶不再比 t 低,再把今天的下标压入。
注意一个自动成立的性质:留在栈里的温度从底到顶一定递减——因为任何比后来者低的栈顶都会在那一刻被弹掉,能留下的只有更高的。这个单调性正是效率的来源:结算时只需盯着栈顶弹,一旦栈顶不低于当前温度就能立刻停手,因为更深处的温度只会更高,它们还得继续等。这也是「单调栈」这个名字的含义。
弹出时下标差 i - j 为什么就是正确答案
当第 i 天把栈里的第 j 天弹出时,i 恰好是 j 之后第一个更高温的日子:j 与 i 之间的每一天都曾经入栈,却没能把 j 弹出,说明它们的温度都不超过第 j 天——中间没有任何一天满足条件,所以等待天数就是 i - j,不多不少。这也解释了栈里为什么必须存下标而不是温度:答案是位置差,只有温度算不出天数。
复杂度怎么数,边界坑在哪
时间 O(n):虽然循环里套着 while,但每个下标一生至多入栈一次、出栈一次,总操作次数是 2n 级别。空间 O(n):最坏温度一路走低,所有下标都压在栈里。
两个易错点:一是弹栈条件必须是严格大于,温度相等不算「更高温」,相等的那天还得继续等;二是扫完后仍留在栈里的日子,到最后也没等来更高温,它们的答案就是初始化的 0,不需要再补处理。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这条:栈顶一旦被「踩过头」就弹出结算,差值就是答案。
轮到第 0 天(73°)。栈是空的,没有更早的天在等更高温,直接进栈。
没有更低的栈顶要结算了,把第 0 天(73°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 1 天(74°)。先看栈顶第 0 天(73°):今天更热,栈顶终于等到了更高温。
第 1 天(74°)比第 0 天(73°)高,第 0 天等到了:弹出它,等待天数 = 1 − 0 = 1。继续看新栈顶还会不会被踩过头。
没有更低的栈顶要结算了,把第 1 天(74°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 2 天(75°)。先看栈顶第 1 天(74°):今天更热,栈顶终于等到了更高温。
第 2 天(75°)比第 1 天(74°)高,第 1 天等到了:弹出它,等待天数 = 2 − 1 = 1。继续看新栈顶还会不会被踩过头。
没有更低的栈顶要结算了,把第 2 天(75°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 3 天(71°)。先看栈顶第 2 天(75°):今天没它热,它继续等,今天先入栈排队。
没有更低的栈顶要结算了,把第 3 天(71°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 4 天(69°)。先看栈顶第 3 天(71°):今天没它热,它继续等,今天先入栈排队。
没有更低的栈顶要结算了,把第 4 天(69°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 5 天(72°)。先看栈顶第 4 天(69°):今天更热,栈顶终于等到了更高温。
第 5 天(72°)比第 4 天(69°)高,第 4 天等到了:弹出它,等待天数 = 5 − 4 = 1。继续看新栈顶还会不会被踩过头。
第 5 天(72°)比第 3 天(71°)高,第 3 天等到了:弹出它,等待天数 = 5 − 3 = 2。继续看新栈顶还会不会被踩过头。
没有更低的栈顶要结算了,把第 5 天(72°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 6 天(76°)。先看栈顶第 5 天(72°):今天更热,栈顶终于等到了更高温。
第 6 天(76°)比第 5 天(72°)高,第 5 天等到了:弹出它,等待天数 = 6 − 5 = 1。继续看新栈顶还会不会被踩过头。
第 6 天(76°)比第 2 天(75°)高,第 2 天等到了:弹出它,等待天数 = 6 − 2 = 4。继续看新栈顶还会不会被踩过头。
没有更低的栈顶要结算了,把第 6 天(76°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
轮到第 7 天(73°)。先看栈顶第 6 天(76°):今天没它热,它继续等,今天先入栈排队。
没有更低的栈顶要结算了,把第 7 天(73°)压入栈顶,等后面更热的一天来踩它。此刻栈里温度始终是从底到顶递减的。
数组扫完,栈里剩下的第 6、7 天到最后也没遇到更高温,它们的 answer = 0。最终 answer = [1,1,4,2,1,1,0,0]。
升序/降序/相等三种边界先想清。
两个高频追问,单调栈是一整类题的模板。
参考代码
def dailyTemperatures(T): ans = [0] * len(T) stack = [] # 存下标,温度递减 for i, t in enumerate(T): while stack and t > T[stack[-1]]: j = stack.pop() ans[j] = i - j stack.append(i) return ans复杂度
- 时间:O(n),每个下标进出栈各一次
- 空间:O(n),最坏全部递减,栈装下所有下标
易错点
面试追问把动画讲成自己的话
追问为什么单调栈能做到 O(n)?
追问如果改成「下一个更高温的温度值」呢?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
车队
LeetCode 853 · 中等 · 沿着 栈 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题