LeetCode刷题记录----739.每日温度(Medium)
2025/9/11
题目(Medium):
我的思路:
暴力遍历比大小:
最简单直观的算法,直接从当前位置往后遍历,找到第一个比当前温度高的温度所在的位置。然后用它的位置减去当前元素所在位置即可
具体代码如下:
class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {int curIndex = 0;int n = temperatures.size();vector<int> res(n, 0);for(int i = 0; i < n; i++){for(int j = i+1; j < n; j ++){if(temperatures[j] > temperatures[i]){res[i] = j - i;break;}}}return res;}
};
时间复杂度:O(N^2)【最糟糕的情况是每个位置都需要遍历到数组末尾才能查找到】
空间复杂度:O(1)
虽然可以,但是会超时。
优化思路:
1.优化的暴力解法
我们可以观察到,温度的范围值是[30,100]这个固定的区间内。因此我们可以直到对于每个温度t,可以比它高的温度范围是[t+1, 100](100度的话肯定不存在比它更高的温度了)。
因此我们可以用一个数组next来记录[30,100]这个区间里的温度第一次出现的位置。从而当我们需要得到当前温度t的下一个更高温的索引的时候,去next中检索到最小的索引值即可。
但是我们要怎么保证我们在温度t位置的时候,next数组中记录的索引值就是对于t之后的更高温度的最小索引位置呢?我们可以通过倒着遍历温度数组的时候更新next数组的方式。这样就可以保证里面的最小索引值只是对当前的温度而言的。
具体代码如下:
class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {//1.next数组标记每个温度值第一次出现的位置int n = temperatures.size();vector<int> res(n), next(101, INT_MAX);//倒着遍历for(int i = n-1; i >=0; i--){int warmIndex = INT_MAX; //比当前位置更温暖的下标//先去里面查找最小的下标值for(int j = temperatures[i] + 1; j <= 100; j++){warmIndex = min(warmIndex, next[j]); //找到[t+1, 100]这个范围中最小的下标值}if(warmIndex != INT_MAX){//说明找到了res[i] = warmIndex - i;}//更新当前的next数组next[temperatures[i]] = i;}return res;}
};
时间复杂度:O(MN)【M是每次遍历[t+1,100]范围的next数组的时候的开销,N是便遍历完整个温度数组的开销】
空间复杂度:O(M)【next数组的开销】
再怎么样M的长度是固定的,不会因为N越来越大而导致时间复杂度指数级增长,因此比我的那个在N大的情况下更好的
2.单调栈
因为我们知道,当前位置的更高温度的答案值是由【比当前温度更高的后面某天的索引值-当前天索引值】得到的,因此我们解题的关键在于由后面得到和索引值两个词中。
由后面得到暗示我们温度的比较有一种后进先出进行比较的特性,因此符合栈的操作规律。而比较完之后如何得出天数值就需要索引值进行相减,因此我们可以知道往栈中压入索引值可以是我们需要的信息。
那接下来我们就需要定义一下入栈和出栈的规矩,遍历温度数组并进行以下操作:
①当栈为空的时候,把当前位置索引值入栈
②当栈不为空的时候,把当前位置温度和栈顶元素比较,如果比栈顶元素小,就也把索引值i入栈;如果比栈顶元素大,那就让栈顶元素preIndex弹出来,并更新栈顶元素对应的答案值 = i - preIndex,一直重复直到栈顶元素大于 等于 当前元素了 或者 栈为空了为止
class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {//利用单调栈int n = temperatures.size();vector<int> res(n);stack<int> stk; //单调栈,保证内部从底到顶是单调递减的,遇到破坏递减的数要加入的时候弹出直到重新递减了为止for(int i = 0; i < n; i++){if(stk.empty()){//栈为空,直接压入索引值(不必压入数值,因为可以根据索引值从数组中获取,同时又保留位置信息stk.push(i);}else{//栈不为空,要判断是否破坏了当前栈的单调性while(!stk.empty() && temperatures[i] > temperatures[stk.top()]){//破坏了,把所有比当前温度更低的索引值弹出来int preIndex = stk.top();stk.pop();res[preIndex] = i - preIndex;}stk.push(i);}}return res;}
};
时间复杂度:O(N)【一次遍历就可以解决】
空间复杂度:O(N)【最坏需要N长度的空间来存储】
下面还有个进一步精简的版本,逻辑完全一样的也,只是合并了一些分支:
class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {//利用单调栈int n = temperatures.size();vector<int> res(n);stack<int> stk; //单调栈,保证内部从底到顶是单调递减的,遇到破坏递减的数要加入的时候弹出直到重新递减了为止for(int i = 0; i < n; i++){//确保当前栈是单调递减的while(!stk.empty() && temperatures[i] > temperatures[stk.top()]){//破坏了,把所有比当前温度更低的索引值弹出来int preIndex = stk.top();stk.pop();res[preIndex] = i - preIndex;}//压入栈stk.push(i);}return res;}
};
总结:
①对于一些题目,即使用暴力解法,也要注意一下题目给出的一些限制条件。通过利用它们能够让我们的暴力解法稍微得到一点点优化
②对于要比较后面的值和当前值的大小,并关联到索引值的时候,可以通过最小栈来解决。并在栈中压入最需要的索引值作为记录。因为我们可以轻易由索引值得到温度值,但是没办法很快用温度值得到索引值。