1.题目
题目地址(739. 每日温度 - 力扣(LeetCode))
https://leetcode.cn/problems/daily-temperatures/
题目描述
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 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 <= 105
30 <= temperatures[i] <= 100
2.题解
2.0 暴力枚举()
思路
两重for循环暴力枚举即可, 这里由于默认数组值为0,所以不单独讨论不存在更大温度的情况
代码
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n);
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
if(temperatures[i] < temperatures[j]){{
ans[i] = j - i;
break;
}}
}
}
return ans;
}
};
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(n^2)\)
- 空间复杂度:\(O(1)\)
2.1 暴力枚举(打表,利用变量范围有限)
思路
由于温度范围在 [30,100] 之内,因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组 next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。
这里遍历大于当前温度的所有可能值,如果在next数组中出现索引值,赋值给warmerIndex,并且使用min不断寻找,就可以找到最小索引
这里必须是反向遍历该数组,才能保证是第一次出现的下标,否则正向遍历可能是之后出现的覆盖了第一次出现的下标
代码
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;
}
};
复杂度分析
-
时间复杂度:\(O(nm)\),其中\(n\)是温度列表的长度,\(m\)是数组 next 的长度,在本题中温度不超过 100,所以\(m\)的值为 100。反向遍历温度列表一遍,对于温度列表中的每个值,都要遍历数组 next 一遍。
-
空间复杂度:\(O(m)\),其中\(m\)是数组 next 的长度。除了返回值以外,需要维护长度为\(m\)的数组 next 记录每个温度第一次出现的下标位置
2.2 单调栈(正常单调栈思路:求点左侧更小/大,顺序遍历; 求点右侧最小/大,逆序遍历)
思路
这里实际上就是求最近更大值的问题,使用单调栈的思路(相当于将2.0中j遍历右半部分那部分替换为用栈存储更大值信息,并进行了筛选(不可能再为更大值的全部弹出,保证每个元素只入栈出栈一次!!!))
由于是向点的右侧寻找更大值,我们选择逆序遍历,这样我们能事先知道右侧的情况,并根据右侧生成的单调栈判断距离当前点最近的更大值
我们选择记录索引的方式,相差天数使用索引相减即可
备注:这里在求天数的时候,我最开始有一个奇思妙想,记录每次while循环的出栈次数就是相差天数,但是这很显然是错误的,因为出栈次数中有部分元素不是在当前点的情况下出栈的,这样就会少算这部分元素
代码
- 语言支持:C++
C++ Code:
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
stack<int> stk;
vector<int> ans(n);
// 生成单调栈
for(int i = n - 1; i >= 0; i--){
int temp = temperatures[i];
while(!stk.empty() && temp >= temperatures[stk.top()]){
stk.pop();
}
ans[i] = stk.empty()?0: (stk.top() - i);
stk.push(i);
}
return ans;
}
};
复杂度分析
-
时间复杂度:\(O(n)\),其中\(n\)是温度列表的长度。逆向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作。
-
空间复杂度:\(O(n)\)。其中\(n\)是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。
2.3单调栈(求点右侧最小/大,正序遍历)
思路
这里相当于逆转思路,在2.2中我们将当前元素作为操作元素,生成了其后面的单调栈,便于比较操作
这里我们将单调栈中的元素作为操作元素,正序遍历,一旦在之后的元素出现了大于栈顶元素的值,意味着:
1.出现了栈顶元素的右侧最近更大值
2.该元素之后不可能再被作为右侧最近更大值的候选项(有更符合的值:当前项)
代码
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;
}
};
复杂度分析
-
时间复杂度:\(O(n)\),其中\(n\)是温度列表的长度。正向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作。
-
空间复杂度:\(O(n)\)。其中\(n\)是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。