【力扣100题】85.每日温度

一、题目描述

给定一个整数数组 temperatures,表示每天的温度,返回一个数组 answer,其中 answeri 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 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 <= temperaturesi <= 100

二、解题思路总览

拿到这道题,首先思考:如何找到每个元素"右边第一个比它大的元素"?

直观的暴力解法是 O(n^2):

  • 对于每个元素,向右遍历找到第一个更大的
  • 最坏情况 O(n2),105 数据会超时

关键洞察: 想象一下排队买饭,如果你是倒数第一个人,后面没人了,你的等待天数是 0。但如果前面有个比你高的人,你就不用一直往后找了。

核心思路: 利用单调递减栈(从栈底到栈顶,温度单调递减)。

步骤 思路 说明
1 从右往左遍历 右侧先处理完,左侧才能确定答案
2 维护单调递减栈 栈中存索引,温度依次递减
3 弹栈找更大元素 弹出比当前温度小的,遇到更大的停止
4 计算距离 ansi = 栈顶索引 - i

为什么从右往左遍历?

对比 从左往右遍历 从右往左遍历
处理顺序 前面不知道后面 后面已经处理完,前面可以直接用
栈维护 需要记录"等待更高温的天数" 栈顶就是答案,直接计算

三、完整代码(方法一:单调递减栈)

cpp 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n);
        stack<int> st;  // 存索引,温度单调递减

        for (int i = n - 1; i >= 0; i--) {
            int t = temperatures[i];
            // 弹栈:栈顶温度 <= 当前温度,说明栈顶不是答案
            while (!st.empty() && t >= temperatures[st.top()]) {
                st.pop();
            }
            // 如果栈不为空,栈顶就是右边第一个更大元素
            if (!st.empty()) {
                ans[i] = st.top() - i;
            }
            // 当前索引入栈
            st.push(i);
        }
        return ans;
    }
};

四、其他解法

方法二:从左往右遍历(维护"等待表")
cpp 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n, 0);
        stack<int> st;  // 存等待更高温的索引,温度单调递增

        for (int i = 0; i < n; i++) {
            while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
                // 当前温度比栈顶的等待者更高,栈顶的等待结束
                ans[st.top()] = i - st.top();
                st.pop();
            }
            st.push(i);  // 当前索引入栈等待更高温
        }
        return ans;
    }
};

思路差异: 从左往右时,栈中存的是"等待更高温的索引",遇到更高温度时直接结算答案。


方法三:暴力解法(O(n^2),仅作对比)
cpp 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n, 0);

        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (temperatures[j] > temperatures[i]) {
                    ans[i] = j - i;
                    break;
                }
            }
        }
        return ans;
    }
};

问题: 两层循环,最坏 O(n2),105 数据会超时。仅作对比,展示单调栈优化的必要性


方法四:预处理 + 二分查找(不推荐)
cpp 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n, 0);
        vector<int> sortedIdx(n);
        iota(sortedIdx.begin(), sortedIdx.end(), 0);
        sort(sortedIdx.begin(), sortedIdx.end(),
             [&](int a, int b) { return temperatures[a] < temperatures[b]; });

        for (int i = 0; i < n; i++) {
            // 二分查找 temperatures[i] 的下一个更大元素
            //省略实现...二分本身也是 O(log n),但整体更复杂
        }
        return ans;
    }
};

问题: 复杂度和实现难度都高于单调栈,不推荐


五、算法流程图

以输入 temperatures = [73,74,75,71,69,72,76,73] 为例,逐步展示算法执行过程:

复制代码
初始状态:
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
        indices:  0    1    2    3    4    5    6    7
        ans:    [0,   0,   0,   0,   0,   0,   0,   0]
        st: [空]

Step 1: i=7, t=73
        - st空,ans[7]=0(默认)
        - st.push(7)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                                           ^
        st: [7] (温度: 73)

Step 2: i=6, t=76
        - t(76) >= temps[st.top()](73),弹出7
        - st空,ans[6]=0(默认)
        - st.push(6)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                                      ^
        st: [6]      (温度: 76)

Step 3: i=5, t=72
        - t(72) >= temps[st.top()](76)? 否,停止弹栈
        - ans[5] = st.top() - 5 = 6 - 5 = 1
        - st.push(5)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                                   ^
        st: [6, 5]  (温度: 76, 72)

Step 4: i=4, t=69
        - t(69) >= temps[5](72)? 否,停止弹栈
        - ans[4] = st.top() - 4 = 5 - 4 = 1
        - st.push(4)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                                ^
        st: [6, 5, 4]  (温度: 76, 72, 69)

Step 5: i=3, t=71
        - t(71) >= temps[4](69)? 是,弹出 4
        - t(71) >= temps[5](72)? 否,停止弹栈
        - ans[3] = st.top() - 3 = 5 - 3 = 2
        - st.push(3)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                             ^
        st: [6, 5, 3]  (温度: 76, 72, 71)

Step 6: i=2, t=75
        - t(75) >= temps[3](71)? 是,弹出 3
        - t(75) >= temps[5](72)? 是,弹出 5
        - t(75) >= temps[6](76)? 否,停止弹栈
        - ans[2] = st.top() - 2 = 6 - 2 = 4
        - st.push(2)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
                    ^
        st: [6, 2]  (温度: 76, 75)

Step 7: i=1, t=74
        - t(74) >= temps[2](75)? 否,停止弹栈
        - ans[1] = st.top() - 1 = 2 - 1 = 1
        - st.push(1)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
              ^
        st: [6, 2, 1]  (温度: 76, 75, 74)

Step 8: i=0, t=73
        - t(73) >= temps[1](74)? 否,停止弹栈
        - ans[0] = st.top() - 0 = 1 - 0 = 1
        - st.push(0)
        temps: [73, 74, 75, 71, 69, 72, 76, 73]
             ^
        st: [6, 2, 1, 0]  (温度: 76, 75, 74, 73)

最终结果:
        ans = [1, 1, 4, 2, 1, 1, 0, 0] ✓

六、逐行解析(方法一)

cpp 复制代码
vector<int> ans(n);
stack<int> st;

初始化: ans 预设为0(表示没有更高温),st 存索引(便于计算距离)。


cpp 复制代码
for (int i = n - 1; i >= 0; i--) {

逆序遍历: 从右往左处理,这样栈顶始终是"当前元素右边第一个更大元素"的位置索引。


cpp 复制代码
    int t = temperatures[i];
    while (!st.empty() && t >= temperatures[st.top()]) {
        st.pop();
    }

弹栈逻辑: 当前温度 t 如果大于等于栈顶温度对应的元素,说明栈顶元素找到了更低温(或等于的温度),它不再是答案,弹出。继续弹直到栈顶元素温度更高。


cpp 复制代码
    if (!st.empty()) {
        ans[i] = st.top() - i;
    }

计算答案: 栈顶索引减去当前索引,就是等待天数。


cpp 复制代码
    st.push(i);
}

入栈: 当前索引入栈,它可能成为左边元素的答案。


七、复杂度分析

各方法复杂度对比
方法 时间复杂度 空间复杂度 说明
方法一:单调递减栈(逆序) O(n) O(n) 标准解法
方法二:单调递增栈(正序) O(n) O(n) 思路对称
方法三:暴力解法 O(n^2) O(1) 不可用,超时
方法四:预处理+二分 O(n log n) O(n) 不推荐,更复杂

核心: 方法一和二是 O(n),因为每个元素最多入栈一次、出栈一次。

方法一详细复杂度分析

时间复杂度:O(n)

  • 遍历一次:O(n)
  • 每个索引最多入栈一次、出栈一次
  • 总弹栈次数 <= 总入栈次数 = O(n)

空间复杂度:O(n)

  • 最坏情况:温度单调递增,所有索引都入栈
  • 例如:30,31,32,33,...

弹栈分析:

情况 弹栈次数 说明
单调递增温度 O(n) 每次都弹出前一个
单调递减温度 0 一次都不弹
随机温度 均摊 O(1) 总弹栈次数 = O(n)

八、面试追问 FAQ

问题 回答要点
Q1:为什么要用栈?不用行吗? 可以用暴力 O(n^2),但会超时。栈的本质是"记录等待更高温的索引",避免重复遍历。
Q2: 从左往右和从右往往哪个更好? 两种都可以,思路对称。逆序更符合直觉(右边先确定),正序更自然(正常遍历顺序)。
Q3: 如果要找"左边第一个更大元素"怎么办? 改成正序遍历,栈维护"单调递减"即可。核心是"当前元素需要找的答案在栈顶"。
Q4: 单调栈能解决哪些其他问题? 柱状图中最大的矩形、接雨水、去除重复字母等。单调栈是处理"下一个更大/更小元素"问题的通用技巧。
Q5: 面试时推荐哪种方法? 首选方法一(逆序单调递减栈),代码简洁高效。如果面试官问,再补充方法二(正序单调递增栈)。
Q6: 如果温度相等怎么办? 本题要求"更高温度",相等不算。所以 t >= temperatures[st.top()] 时弹栈是正确的。

九、相关题目

题号 题目 难度 关联点
739 每日温度 中等 本题
496 下一个更大元素 I 简单 单调栈入门
503 下一个更大元素 II 中等 环形数组扩展
42 接雨水 困难 单调栈综合应用
84 柱状图中最大的矩形 困难 单调栈综合应用
316 去除重复字母 困难 单调栈变形

推荐刷题顺序:

  1. 本题(739.每日温度)→ 单调栈入门
  2. 496(下一个更大元素 I)→ 简单变种
  3. 503(下一个更大元素 II)→ 环形数组
  4. 84(柱状图中最大的矩形)→ 综合应用

十、总结

维度 内容
考察知识点 单调栈、-next Greater Element
难度 中等
核心思维 用栈维护"等待更高温的索引",O(1) 找到答案
关键技巧 逆序遍历 + 单调递减栈
推荐解法 方法一(逆序单调递减栈)或方法二(正序单调递增栈)
变形题 下一个更小元素、左边第一个更大元素、环形数组

一句话总结: 单调栈的核心是"栈顶元素是当前元素要找的答案",维护单调性保证每个元素最多入栈一次、出栈一次,达到 O(n) 时间复杂度。


相关推荐
Coder-magician2 小时前
《代码随想录》刷题打卡day15:二叉树part05
数据结构·c++·算法
Kurisu_红莉栖2 小时前
力扣56合并区间
算法·leetcode
Irissgwe2 小时前
算法的时间复杂度和空间复杂度
数据结构·c++·算法·c·时间复杂度·空间复杂度
随意起个昵称2 小时前
区间dp-基础题目3(永别)
c++·算法
周末也要写八哥2 小时前
有向图Hierholzer算法的另一种实现
算法
bIo7lyA8v2 小时前
算法调优中的性能回归与基准测试分析的技术8
算法·数据挖掘·回归
有点。2 小时前
C++贪心算法二(练习题)
c++·算法·贪心算法
西安邮电大学2 小时前
贪心算法详细讲解
java·后端·其他·算法·面试
开源Z2 小时前
LeetCode 135 · 分发糖果:两次扫描,先左后右取最大
算法·leetcode