LeetCode 739中等单调栈
每日温度 图解题解
每天要等几天才会更热?单调栈一次扫描,温度进来时顺手把答案一口气全填了。
栈里存的是还没找到「更热那天」的日子的**下标**,从底到顶对应递减温度。新温度进来,只要它比栈顶那个下标对应的温度高,就把栈顶弹出结算:答案 = 当前下标 − 弹出的下标,算的就是等了几天。弹完如果还比新栈顶高,继续弹、继续填答案;弹完或栈空后,把**当前下标**压进去,等着将来更高的温度来叫醒自己。存下标而非温度,是因为「等了几天」要用两个位置做减法,只有下标才能算差值。
这道题到底在问什么
对每天温度,求要等几天才会遇到更高温;没有则为 0。
- temperatures
- [73,74,75,71,69,72,76]
- 输出
- [1,1,4,2,1,1,0]
最优解:一步一步想明白
- 3如果对每一天都往后找更暖的一天,很多位置会被反复看。
- 4新温度比栈顶高时,栈顶那天终于等到了答案,弹出并填天数。
- 5stack=[0]柱状图比纯栈更直观:73 还没有看到右边更高温,先入栈等待。
- 6ans[0]=174 比栈顶 73 高,所以第 0 天等到了答案:1-0=1。先弹出 0。
- 7stack=[1]结算完别人后,74 自己也要等右边更高温,于是下标 1 入栈。弹和压分开,动作不会挤成一帧。
- 875>74 结算第1天;71、69 无更高温 → 压栈等待快进三天。第 2 天 75 比 74 高,第 1 天等到答案、74 弹出,75 自己入栈。接着第 3 天 71、第 4 天 69 一个比一个低,谁也没遇到更高温,依次压进栈里。现在栈里从底到顶是 75、71、69,温度递减——等会儿一个高温会把它们连着叫醒。
- 9ans[4]=1来到 72,先看栈顶 69。72 更高,所以第 4 天答案是 1,弹出 4。
- 10ans[3]=2弹完 69 后继续看新的栈顶 71。72 也比 71 高,所以第 3 天答案是 5-3=2。
- 11结算完别人,72 也入栈等更高温72 把 69、71 都结算完后,自己也没遇到更高温,于是下标 5 入栈。现在栈里从底到顶是 75、72,又是严格递减——这正是下一个高温能连弹的前提。
- 12while 连续弹最后 76 继续结算:先让 72 得到答案 1,再让 75 得到答案 4。连续弹栈现在能逐步看出来。
- 13栈从底到顶严格递减(75>72) → 76 来了,while 连弹两人回放刚才那一下,看清单调栈的灵魂。来到第 6 天 76,栈里从底到顶是 75、72,温度严格递减——这是不变量。76 比栈顶 72 高,结算第 5 天:6 减 5 等于 1,弹出;再看新栈顶 75,76 还是更高,结算第 2 天:6 减 2 等于 4,再弹。一个高温用一个 while 循环,把压在它下面、所有比它低的等待者一次性叫醒——这就是 while 不能写成 if 的原因。
- 16叫醒一个,就能立刻填出那天的等待天数。
- 19strict >[30,30] 第二天不是更暖,只是相等。第 0 天答案仍然是 0。
- 24这题学完不要乱跳,下一步去 对应专题 连刷同类题;卡住时用 AI 答疑追问“为什么这一步成立”,比只背答案更稳。
⚠️ 容易写错的地方
✗ 错:栈里存温度
✓ 对:栈里存下标
答案需要 i-j 的天数差。
✗ 错:遇到相等温度也弹
✓ 对:只在更高温时弹
题目要求 warmer,不是 warmer or equal。
✗ 错:只弹一次
✓ 对:while 连续弹
新温度可能结算多个等待日。
完整代码(Python / C++ / Java)
Python
class Solution:
def dailyTemperatures(self, temperatures):
ans = [0] * len(temperatures)
st = []
for i, t in enumerate(temperatures):
while st and temperatures[st[-1]] < t:
j = st.pop()
ans[j] = i - j
st.append(i)
return ansC++
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> ans(temperatures.size());
vector<int> st;
for (int i = 0; i < temperatures.size(); i++) {
while (!st.empty() && temperatures[st.back()] < temperatures[i]) {
int j = st.back(); st.pop_back();
ans[j] = i - j;
}
st.push_back(i);
}
return ans;
}
};Java
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] ans = new int[temperatures.length];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < temperatures.length; i++) {
while (!st.isEmpty() && temperatures[st.peek()] < temperatures[i]) {
int j = st.pop();
ans[j] = i - j;
}
st.push(i);
}
return ans;
}
}套路模板 · 每日温度迁移骨架
# 单调栈模板:当前元素负责结算左边等待的人
stack = [] # 通常存下标,不只存值
for i, x in enumerate(nums):
while stack and x 能让 nums[stack[-1]] 得到答案:
j = stack.pop()
ans[j] = i - j 或 用 i/j 计算贡献
stack.append(i)
return ansvector<int> st;
for (int i = 0; i < nums.size(); i++) {
while (!st.empty() && nums[i] 能结算 nums[st.back()]) {
int j = st.back(); st.pop_back();
ans[j] = i - j; // 或计算贡献
}
st.push_back(i);
}
return ans;Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
while (!stack.isEmpty() && nums[i] 能结算 nums[stack.peek()]) {
int j = stack.pop();
ans[j] = i - j; // 或计算贡献
}
stack.push(i);
}
return ans;复杂度
时间复杂度
O(n)
每个下标最多入栈一次、出栈一次
空间复杂度
O(n)
最坏温度递减,所有下标都在栈里
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 每日温度 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
栈为什么是单调递减的?+
栈里从底到顶都是还没被更高温结算的日子,温度越往顶越低,后来的低温压在上面继续等。
为什么用 while 而不是 if?+
一个高温可能一次性结算压在它下面的多个较低温日子,必须循环弹到栈顶不再更低为止。
和「下一个更大元素」(Next Greater Element) 什么关系?+
本质同一道题:每个位置找右侧第一个更大的元素,只是这里把答案从「值」换成了「距离」。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。