从零开始刷算法-单调栈-每日温度
一、题目概述
题目来源:LeetCode 739 - Daily Temperatures
题目要求:
给定一个整数数组 temperatures,表示每天的温度,返回一个数组 answer,其中 answer[i] 表示距离第 i 天之后,温度升高所需要的天数。如果之后没有更高的温度,则 answer[i] = 0。
示例:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
二、思路分析
这道题的核心在于:
对于每一天
i,要找到右边第一个比它温度高的那一天。
如果我们暴力查找,每个 i 都向右扫描,复杂度是 O(n^2),显然不行。
于是可以考虑使用一个**单调栈(Monotonic Stack)**来优化。
三、单调栈思路讲解
1️⃣ 从后往前遍历的原因
我们从右往左遍历数组,因为:
当前天的右边温度都已经处理完;
栈中存放的下标对应的温度都是“右边的天”,
所以可以直接用栈顶判断“右边第一个更高温度”。
2️⃣ 栈中存什么?
栈中存放的是 下标 i,而不是温度值。
因为要计算天数差(st.top() - i)。
3️⃣ 如何维护栈的单调性?
我们希望栈中的温度是递减的(栈顶最小)。
当前温度
t如果比栈顶温度高,说明栈顶那天不可能成为任何左边天的答案,直接pop掉;否则说明栈顶那天温度比当前天高,可以计算答案。
4️⃣ 状态更新逻辑
伪代码逻辑如下:
for i 从右往左:当前温度 = t[i]while 栈不空 && 当前温度 >= 栈顶温度:弹栈(因为这些天对答案没贡献)if 栈不空:ans[i] = 栈顶下标 - i压入当前下标 i
四、完整代码实现
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;}
};
五、运行流程示例
以输入 [73,74,75,71,69,72,76,73] 为例:
| i | 温度 | 栈中下标(从栈底到栈顶) | 栈中对应温度 | ans[i] | 说明 |
|---|---|---|---|---|---|
| 7 | 73 | [7] | [73] | 0 | 栈空,压入 |
| 6 | 76 | [] | [] | 0 | 76>73,弹出后压入 |
| 5 | 72 | [6,5] | [76,72] | 1 | 栈顶温度76更高 |
| 4 | 69 | [6,5,4] | [76,72,69] | 1 | 栈顶温度72更高 |
| 3 | 71 | [6,5,3] | [76,72,71] | 2 | 下一个高温是72 |
| 2 | 75 | [6,2] | [76,75] | 4 | 下一个高温是76 |
| 1 | 74 | [6,2,1] | [76,75,74] | 1 | 下一个高温是75 |
| 0 | 73 | [6,2,1,0] | [76,75,74,73] | 1 | 下一个高温是74 |
六、复杂度分析
时间复杂度:
O(n),每个元素最多被压栈和弹栈一次。空间复杂度:
O(n),栈最多存储 n 个下标。
七、总结
| 特点 | 说明 |
|---|---|
| 遍历方向 | 从右往左更自然 |
| 栈结构 | 单调递减栈 |
| 栈中存储 | 温度下标 |
| 逻辑核心 | 弹出无用天,栈顶即为最近更高温度 |
| 时间复杂度 | O(n) |
💬 一句话总结:
单调栈的关键不是“栈”本身,而是“单调性”带来的剪枝。
我们用栈保证每次处理时,能立刻找到“右边第一个更大元素”。
