力扣刷题#24-0739-每日温度

力扣刷题#24-0739-每日温度

题目

给定一个整数数组 temperatures,表示每天的温度。返回一个数组 answer,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果之后都不会升高,用 0 代替。

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

我的思路

核心直觉:从右往左扫,用栈维护"候选者"

看到"下一个更大的"这类字眼,直觉告诉我应该用栈。但要怎么扫、栈里存什么?

从右往左遍历,因为"右边"的信息对左边的答案有用——我先知道右边的情况,再回来填左边的答案。

栈的作用:保存那些比当前温度高、且位置在右边的日子。对"左边的每一天"来说,这些就是"右侧第一个更热的日子"的候选者。

栈里的数据需要满足一个性质:从栈底到栈顶,温度严格递减。如果有个日子温度又低位置又远,那它永远不会被任何"左边日子"选中——弹掉。


算法三步走

从右往左遍历每一天 i:

① 弹:while (栈不空 且 栈顶温度 ≤ 今天温度) → pop
     理由:今天的温度更高且更靠左,被弹掉的日子比今天冷、比今天远,
           以后左边日子找"右边第一个更热的",今天一定抢先被选中

② 算:if (栈不空) → answer[i] = 栈顶索引 - i
     栈顶就是弹剩下的"第一个比今天热的日子"

③ 压:s.push(i),今天的索引入栈,等左边更冷的日子来查

一步步走通

[73, 74, 75, 71, 69, 72, 76, 73],从右往左:

i 当天温度 ① 弹栈 弹后栈 ② answer[i] ③ 压后栈
7 73 栈空,跳过 [] 0 [7]
6 76 73≤76→弹7 [] 0 [6]
5 72 76>72→不弹 [6] 6-5=1 [6,5]
4 69 72>69→不弹 [6,5] 5-4=1 [6,5,4]
3 71 69≤71→弹4;72>71→停 [6,5] 5-3=2 [6,5,3]
2 75 71≤75→弹3;72≤75→弹5;76>75→停 [6] 6-2=4 [6,2]
1 74 75>74→不弹 [6,2] 2-1=1 [6,2,1]
0 73 74>73→不弹 [6,2,1] 1-0=1 [6,2,1,0]

输出:[1, 1, 4, 2, 1, 1, 0, 0]

关键观察:栈里的温度始终从底到顶递减。以 i=0 时的栈为例:

栈底 → 索引6(温度76) → 索引2(温度75) → 索引1(温度74) → 索引0(温度73) ← 栈顶
        最热              次热              次次热              当前

这就是"单调栈"名字的由来。


为什么要存索引而不是存温度?

我的第一反应是栈里要不要存 pair<温度, 索引>。后来想通了——答案是"几天后",需要算索引差,所以索引必须存。而温度随时能从原数组查到:

temperatures[s.top()]  // 栈顶对应的温度,O(1)

存温度就冗余了。栈里只存 int 索引即可。


我的 AC 代码

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> s;
        int len = temperatures.size(); 
        vector<int> an(len, 0);
        for (int i = len - 1; i >= 0; i--) {
            while (!s.empty() && temperatures[i] >= temperatures[s.top()]) {
                s.pop();
            }
            if (!s.empty()) {
                an[i] = s.top() - i;
            }
            s.push(i);
        }
        return an;
    }
};

代码逐段解析

第 5 行:初始化 answer 数组

vector<int> an(len, 0);

len 个 0 打底。后面只有找到"右侧第一个更热的"才覆盖,找不到的保持 0——刚好满足题目要求。

第 6-7 行:while 弹栈

while (!s.empty() && temperatures[i] >= temperatures[s.top()]) {
    s.pop();
}

两个注意点:

  • !s.empty() 必须写在前面——&& 从左往右算,栈空时先取 s.top() 就崩了。
  • >= 不用 >:如果右边有个相同温度的日子,它更靠右 → 对"左边"来说不是"更热的" → 也得弹掉。

第 8-11 行:if 算答案 + push

if (!s.empty()) {
    an[i] = s.top() - i;
}
s.push(i);

if 条件和 s.push(i) 是分开的——不管今天有没有找到右侧更热的日子,今天的索引都必须进栈,等着被左边更冷的日子查询。


踩坑记录

坑 1:s.push(i) 写在了 if 里面

// ❌ 第一版
if (!s.empty() && temperatures[i] < temperatures[s.top()]) {
    an[i] = s.top() - i;
    s.push(i);       // ← push 受 if 约束
}

后果:while 弹空栈后,s.empty() 为 true,if 不走 → s.push(i) 不执行 → 栈永远是空的 → 后面所有 answer[i] 全是 0。

修复s.push(i) 放到 if 外面,作为每轮必定执行的第三步。

坑 2:vector::insert 误用

// ❌
an.insert(s.top() - i);  // insert(pos, val) 需要两个参数
// ✅
an[i] = s.top() - i;     // 直接按下标赋值

insert 是"在指定位置插入"不是你想要的"填指定位置"。数组 an 已提前分配好 len 个位置,直接用 an[i] = ... 赋值即可。

坑 3:&& 短路求值顺序

// ❌ 先算 s.top() 后判空
while (temperatures[i] >= temperatures[s.top()] && !s.empty())
// ✅ 先判空
while (!s.empty() && temperatures[i] >= temperatures[s.top()])

&& 从左往右计算。栈空时前者先执行 s.top() → 空栈取顶 → 崩溃。


复杂度分析

维度 说明
时间复杂度 O(n) 每个索引入栈一次、出栈最多一次
空间复杂度 O(n) 栈最多存 n 个索引

关键点总结

关键点 说明
遍历方向 从右往左,先知道右边才能填左边
栈存什么 只存索引,温度通过数组查
弹栈条件 栈顶温度 ≤ 今天温度 → 弹掉(更冷且更远 = 没用)
单调性 栈从底到顶温度严格递减(单调栈)
push 时机 无论有没有答案,每轮都必须 push

本文档由 AI 辅助生成,作者提供问题,思路和代码,AI仅负责文本修饰,综合获得以上内容。

posted @ 2026-07-13 16:48  STA_running  阅读(1)  评论(0)    收藏  举报