力扣-739. 每日温度

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\)是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。

posted @ 2024-05-17 22:24  DawnTraveler  阅读(22)  评论(0编辑  收藏  举报