LeetCode 739. 每日温度
题目描述

题意分析
对每一天,找到它之后第一个温度严格更高的日期,返回需要等待的天数,也就是两个日期的下标差。不是求温度差,也不是随便找一个更暖的日期。
温度相同不能结束等待;如果后面始终没有更暖的一天,这一天的答案为
0。结果数组与输入等长,每个位置分别对应原来的那一天。
解法:单调栈保存未匹配日期
核心思路
[!blue]
如果从每一天向后逐个寻找,会反复扫描同一段温度。可以反过来考虑:从左到右遇到一个新温度时,它能为之前哪些还在等待的日期确定答案?
用栈保存尚未找到更暖日期的下标,栈底到栈顶的下标递增,对应温度非递增。当前温度高于栈顶时,栈顶日期已经等到了更暖的一天,弹出它,并用当前下标减去旧下标得到等待天数。当前温度还可能高于弹出后的新栈顶,所以要持续检查。
当前日期为什么一定是旧日期之后的第一个更暖日?因为日期按顺序扫描,旧日期一直留在栈中,说明此前没有更暖日为它完成匹配;如今第一次被弹出,就恰好遇到了最近的更暖日。
弹栈停止时,栈为空,或者栈顶温度不低于当前温度。后一种情况下,栈中更下面的温度也不低于栈顶,当前日不可能再解决它们,因此可以停止检查。随后把当前下标入栈,温度的非递增关系仍然成立。扫描结束还留在栈中的日期没有更暖日,保留初始答案
0。
解题步骤
- 创建长度为
n、初值全为0的结果数组,以及保存日期下标的空栈。- 从左到右扫描当前下标
i,比较temperatures[i]与栈顶下标对应的温度。- 只要当前温度严格更高,就弹出
prev,写入res[prev] = i - prev,再检查新的栈顶。- 无法继续弹栈时,将当前下标
i入栈,等待后面更暖的日期。- 扫描结束返回结果,未弹出的日期无需额外处理。
代码实现
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < temperatures.length; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int prev = stack.pop();
// 当前日期是 prev 之后第一个更高温度日期。
res[prev] = i - prev;
}
stack.push(i);
}
return res;
}
}
func dailyTemperatures(temperatures []int) []int {
res := make([]int, len(temperatures))
stack := make([]int, 0)
for i := 0; i < len(temperatures); i++ {
for len(stack) > 0 && temperatures[i] > temperatures[stack[len(stack)-1]] {
prev := stack[len(stack)-1]
stack = stack[:len(stack)-1]
// 当前下标为 prev 找到第一个更高温度。
res[prev] = i - prev
}
stack = append(stack, i)
}
return res
}
复杂度分析
- 时间复杂度:$O(n)$。每个下标恰好入栈一次、至多出栈一次,全部日期的弹栈总次数不超过 $n$;嵌套循环不会让同一下标被重复扫描多次。
- 空间复杂度:$O(n)$。温度始终不升时,所有日期都可能留在栈中;不计返回结果数组本身的空间。
关键点总结
[!green]
- 栈保存未得到答案的日期下标,既能回查温度,也能计算等待天数。
- 弹栈意味着该日期第一次遇到更暖日,答案可以立即确定,之后无需再参与比较。
- 相等温度保留在栈中,弹栈条件必须是严格大于。
- 一个当前温度可能同时解决多个旧日期,因此弹栈使用循环。
易错点总结
[!yellow]
- 把弹栈条件写成
>=,会把同温度的日期误当作更暖日。- 只用一次
if弹栈,当前温度可能已经足够解决更深的多个日期,却没有继续更新它们。- 栈中只存温度,无法区分相同温度出现的位置,也无法得到等待天数。
- 将答案写成温度之差,或者给当前下标写答案;此时确定的是旧下标
prev的下标距离i - prev。- 给最终留在栈中的日期填
-1,与题目要求不符;默认的0就是它们的答案。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 496. 下一个更大元素 I | 简单 | 同样寻找右侧第一个更大元素,本题返回距离,所以栈中保存下标。 |
| 503. 下一个更大元素 II | 中等 | 原题数组首尾相接,需要再次扫描来补足跨边界的更大值,本题只向右查找。 |
| 1019. 链表中的下一个更大节点 | 中等 | 用单调栈确定最近的更大元素;本题返回右侧更大值的下标距离,该题先把链表值转为顺序序列处理。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!