📝 题目描述

题目链接每日温度

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

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
示例 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 <= temperatures[i] <= 100

💡 解题思路

方法一:单调栈

维护一个存储温度下标的栈,从栈底到栈顶,所索引的温度是递减的。

倒序遍历温度数组(倒序入栈 相当于 正序出栈),对于每一个索引 i

  • 只要栈不空,那么查看 temperatures[stack.top()]temperatures[i] 的大小,如果栈顶索引代表的温度小于等于 i 所代表的温度,则出栈(这里可以理解为把数组的元素想象成并列站立的人,元素大小想象成人的身高。这些人站成一列,如何求元素「2」的下一个更大元素呢?很简单,从「2」的位置高度向后看,那么他后面可见的第一个人就是「2」的下一个更大元素,因为比「2」小的元素身高不够,「2」看不见,第一个露出来的就是答案,这里向后看也用到了倒序入栈相当于正序出栈的思想)。
  • 然后开始存储答案,如果栈为空,说明后面没有更高的温度了,存入 0,否则存储 栈顶元素 - i 表示答案。
  • i 入栈。

方法二:暴力法

对于温度列表中的每个元素 temperatures[i],需要找到最小的下标 j,使得 i < jtemperatures[i] < temperatures[j]

由于温度范围在 [30, 100] 之内,因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组 next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。

反向遍历温度列表。对于每个元素 temperatures[i],在数组 next 中找到从 temperatures[i] + 1100 中每个温度第一次出现的下标,将其中的最小下标记为 warmerIndex,则 warmerIndex 为下一次温度比当天高的下标。如果 warmerIndex 不为无穷大,则 warmerIndex - i 即为下一次温度比当天高的等待天数,最后令 next[temperatures[i]] = i

为什么上述做法可以保证正确呢?因为遍历温度列表的方向是反向,当遍历到元素 temperatures[i] 时,只有 temperatures[i] 后面的元素被访问过,即对于任意 t,当 next[t] 不为无穷大时,一定存在 j 使得 temperatures[j] == ti < j。又由于遍历到温度列表中的每个元素时都会更新数组 next 中的对应温度的元素值,因此对于任意 t,当 next[t] 不为无穷大时,令 j = next[t],则 j 是满足 temperatures[j] == ti < j 的最小下标。

🔧 代码实现

1、单调栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> answer(n, 0);
// 构造单调栈
stack<int> greater_temperature_index;
// 倒着入栈 -> 相当于正着出栈
for (int i = n - 1; i >= 0; i--) {
// 只要栈不空,且栈顶小于等于当前元素,就一直出栈
while (!greater_temperature_index.empty() && temperatures[greater_temperature_index.top()] <= temperatures[i]) {
greater_temperature_index.pop();
}
// 保存结果到答案数组
answer[i] = greater_temperature_index.empty() ? 0 : greater_temperature_index.top() - i;
// 入栈
greater_temperature_index.push(i);
}
return answer;
}
};

2、暴力法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n), next(101, INT_MAX);
for (int i = n - 1; i >= 0; --i) {
int warmerIndex = INT_MAX;
for (int t = temperatures[i] + 1; t <= 100; ++t) {
warmerIndex = min(warmerIndex, next[t]);
}
if (warmerIndex != INT_MAX) {
ans[i] = warmerIndex - i;
}
next[temperatures[i]] = i;
}
return ans;
}
};

📊 复杂度分析

1、单调栈

  • 时间复杂度O(n)O(n),其中 nn 是温度列表的长度。正向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作。
  • 空间复杂度O(n)O(n),其中 nn 是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。

2、暴力法

  • 时间复杂度O(nm)O(nm),其中 nn 是温度列表的长度,mm 是数组 next 的长度,在本题中温度不超过 100,所以 mm 的值为 100。反向遍历温度列表一遍,对于温度列表中的每个值,都要遍历数组 next 一遍。
  • 空间复杂度O(m)O(m),其中 mm 是数组 next 的长度。除了返回值以外,需要维护长度为 mm 的数组 next 记录每个温度第一次出现的下标位置。

🎯 总结

  • 核心思想:掌握单调栈的模板写法。