📌 题目链接:739. 每日温度 - 力扣(LeetCode)
🔍 难度:中等 | 🏷️ 标签:数组、单调栈
⏱️ 目标时间复杂度:O(n)
💾 空间复杂度:O(n) (单调栈)或 O(1) (暴力优化版,但本题更推荐单调栈)
🎯 一句话题意:给定每日气温数组,对每一天,求下一次气温更高是在几天后。若没有,则填 0。
这道题是单调栈的经典入门题,也是面试高频考点!它完美展示了“找右边第一个更大元素”这类问题的标准解法。掌握它,等于掌握了单调栈的“灵魂”。
🔍 题目分析
输入是一个长度为 n 的整数数组 temperatures,每个元素代表某天的温度(30 ≤ T ≤ 100)。
要求输出一个同样长度的数组 answer,其中:
answer[i] = j - i,其中j是满足j > i且temperatures[j] > temperatures[i]的最小下标;- 若不存在这样的
j,则answer[i] = 0。
✅ 示例回顾
输入: [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
- 第 0 天(73°)→ 第 1 天(74°)更高 → 等 1 天;
- 第 2 天(75°)→ 要等到第 6 天(76°)才更高 → 等 4 天;
- 第 6、7 天之后无更高温度 → 填 0。
🧱 核心算法及代码讲解:单调栈(Monotonic Stack)
💡 核心思想:用栈维护一个“递减”的下标序列,当遇到一个更大的温度时,就“清算”所有比它小的元素。
📌 为什么用单调栈?
这类“找右边第一个更大/更小元素”的问题,天然适合单调栈:
- 暴力法:对每个 i,向右遍历找第一个更大的 j → O(n²),超时(n=1e5)。
- 单调栈:每个元素最多入栈一次、出栈一次 → O(n) 时间!
📐 单调栈的性质(本题)
- 栈中存的是下标(不是温度值!),便于计算天数差。
- 从栈底到栈顶,温度严格递减(非严格也可,但本题用严格更清晰)。
- 当新元素
temperatures[i]比栈顶对应的温度大时,说明找到了“下一个更高温度”!
🧾 算法步骤(正向遍历)
-
初始化空栈
s和答案数组ans(全 0)。 -
从左到右遍历每一天
i:-
循环检查:只要栈非空 且 当前温度
> 栈顶下标对应温度:- 弹出栈顶
prevIndex; - 设置
ans[prevIndex] = i - prevIndex(这就是等待天数!);
- 弹出栈顶
-
将当前下标
i入栈。
-
-
遍历结束,栈中剩余元素对应的
ans保持为 0(符合题意)。
🧪 为什么正确?
-
关键洞察:当
temperatures[i]大于temperatures[prevIndex]时,i就是prevIndex右边第一个更大的温度!- 因为如果中间存在更近的
j < i使得T[j] > T[prevIndex],那么prevIndex早就被j弹出了。
- 因为如果中间存在更近的
-
单调性保证:栈中元素对应的温度递减,所以一旦当前温度能“打败”栈顶,就能连续打败多个栈顶元素(如示例中 i=6 打败 5 和 2)。
💻 C++ 核心代码(带详细行注释)
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n); // 初始化答案数组,全0
stack<int> s; // 单调栈:存储下标
for (int i = 0; i < n; ++i) {
// 当前温度比栈顶对应温度高 → 找到“下一个更高温度”
while (!s.empty() && temperatures[i] > temperatures[s.top()]) {
int prevIndex = s.top(); // 待更新的下标
ans[prevIndex] = i - prevIndex; // 等待天数 = 当前下标 - 之前下标
s.pop(); // 弹出已处理的下标
}
s.push(i); // 当前下标入栈(无论是否弹出过别人)
}
return ans;
}
✅ 注意:栈中始终维护的是“尚未找到更高温度”的那些天的下标。
🧩 解题思路(分步拆解)
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1️⃣ | 初始化 | 创建答案数组 ans(默认全 0),创建空栈 s |
| 2️⃣ | 正向遍历 | 从 i = 0 到 n-1 |
| 3️⃣ | 比较 & 弹栈 | 若 T[i] > T[s.top()],则不断弹栈并更新 ans[prevIndex] = i - prevIndex |
| 4️⃣ | 入栈 | 将当前 i 压入栈(代表它还没找到更高的温度) |
| 5️⃣ | 返回结果 | 遍历结束,ans 即为所求 |
🔄 类比理解:想象你在排队,每个人举着自己的温度牌。你(当前 i)往前看,如果前面有人温度比你低,你就告诉他:“你的等待结束了,等了
i - 他的位置天!” 然后他离开队伍。你继续往前看,直到遇到温度 ≥ 你的人,然后你站到他后面排队。
📊 算法分析
| 维度 | 分析 |
|---|---|
| 时间复杂度 | O(n) 每个下标最多入栈 1 次、出栈 1 次,总操作 ≤ 2n |
| 空间复杂度 | O(n) 最坏情况(温度递减),栈存所有下标 |
| 适用场景 | 所有“找右侧第一个更大/更小元素”问题 如:接雨水、柱状图最大矩形、股票跨度等 |
| 面试价值 | ⭐⭐⭐⭐⭐ 必考!需能手写 + 讲清单调性 + 举例说明 |
💡 延伸思考:
- 如果题目改成“上一次更高温度”,怎么办?→ 反向遍历 + 单调栈
- 如果温度范围很大(如 1~1e9),还能用方法一(next 数组)吗?→ 不能!必须用单调栈
💻 完整代码
C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n);
stack<int> s;
for (int i = 0; i < n; ++i) {
while (!s.empty() && temperatures[i] > temperatures[s.top()]) {
int previousIndex = s.top();
ans[previousIndex] = i - previousIndex;
s.pop();
}
s.push(i);
}
return ans;
}
};
// 测试
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
Solution sol;
vector<int> t1 = {73,74,75,71,69,72,76,73};
auto res1 = sol.dailyTemperatures(t1);
for (int x : res1) cout << x << " "; // 输出: 1 1 4 2 1 1 0 0\n
cout << "\n";
vector<int> t2 = {30,40,50,60};
auto res2 = sol.dailyTemperatures(t2);
for (int x : res2) cout << x << " "; // 输出: 1 1 1 0\n
cout << "\n";
vector<int> t3 = {30,60,90};
auto res3 = sol.dailyTemperatures(t3);
for (int x : res3) cout << x << " "; // 输出: 1 1 0\n
cout << "\n";
return 0;
}
JavaScript
/**
* @param {number[]} temperatures
* @return {number[]}
*/
var dailyTemperatures = function(temperatures) {
const n = temperatures.length;
const ans = new Array(n).fill(0);
const stack = []; // 存储下标
for (let i = 0; i < n; i++) {
// 当前温度更高,清算栈顶
while (stack.length > 0 && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const prevIndex = stack.pop();
ans[prevIndex] = i - prevIndex;
}
stack.push(i);
}
return ans;
};
// 测试
console.log(dailyTemperatures([73,74,75,71,69,72,76,73])); // [1,1,4,2,1,1,0,0]
console.log(dailyTemperatures([30,40,50,60])); // [1,1,1,0]
console.log(dailyTemperatures([30,60,90])); // [1,1,0]
🌟 本期完结,下期见!🔥
👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!
💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪
📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!