739. 每日温度(C++)单调栈的应用

这篇博客探讨了如何解决739号LeetCode问题——每日温度。博主首先介绍了两种暴力算法,一种是简单的双层循环,另一种是优化后的暴力算法,通过记录每个温度的首次出现下标来减少循环次数。接着,博主详细解释了单调栈的解决方案,包括正向和反向遍历的单调栈,并分析了它们的时间复杂度和空间复杂度。最后,博主强调了单调栈在寻找一维数组中相邻元素关系问题中的应用,并指出其本质是用空间换时间。
摘要由CSDN通过智能技术生成

739. 每日温度

无语了相对简单我还是不会。

日常超时

自己写的暴力算法(超时)

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> result;
        int i,j;
        for(i=0;i<temperatures.size();i++){
            int num = 1;
            for(j=i+1;j<temperatures.size();j++){
                if(temperatures[j]>temperatures[i]){
                    break;
                }
                num++;
            }
            if(j==temperatures.size())num=0;
            result.push_back(num);
        }
        return result;
    }
};

官方写的暴力算法

思想:由于温度在30-100这个区间,所以我们用一个数组来存放每个温度第一次出现的下标。

逆序遍历,每次看到一个温度就记录下来。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();	//记录温度长度
        vector<int> ans(n), next(101, INT_MAX);		// ans为最终结果,而next是存放每个温度第一次出现的下标
        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]);	//这里用min的原因不只是因为INT_MAX,还有一个原因是题目要找的是最先出现的较高温度,当然索引值越低越好啦
            }
            if (warmerIndex != INT_MAX) {	// 如果前面有较高的温度
                ans[i] = warmerIndex - i;
            }
            next[temperatures[i]] = i;		//记录当前温度出现的下标
        }
        return ans;
    }
};

这个暴力优于自己写的暴力算法,是他巧妙的把第二个循环限制在低于100次。

突然想想这个解法也不是没有根据的,我自己写的暴力算法中,举个例子,对于[4,3,2,5],我们每次都循环到5,这中间的数字读了许多遍,这其实是冗余的,没有必要的。想到的方法就是把出现过的记录在一个数组中,然后通过索引直接得出。至于为什么要使用逆序,虽然感受到逆序的好处,但是说不上为什么,不过也不需要钻牛角尖,记住这种逆向思维即可。

  • 时间复杂度:O(nm),其中 n 是温度列表的长度,m 是数组 next 的长度,在本题中温度不超过 100,所以 m 的值为 100。反向遍历温度列表一遍,对于温度列表中的每个值,都要遍历数组 next 一遍。

  • 空间复杂度:O(m),其中 m 是数组 next 的长度。除了返回值以外,需要维护长度为 m 的数组 next 记录每个温度第一次出现的下标位置。

单调栈(正向遍历)

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;
    }
};

复杂度分析

for循环是n次,那while循环是多少呢?其实不用关心,因为对于温度列表中的每个下标,最多有一次进栈和出栈的操作。

  • 时间复杂度:O(n),其中 n 是温度列表的长度。正向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作。

  • 空间复杂度:O(n),其中 n 是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。

单调栈(反向遍历)

根据官方的正向遍历单调栈代码,自己仿写了反向遍历单调栈代码

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n);
        stack<int> s;
        for (int i = n-1; i >= 0; --i) {
            while(!s.empty() && temperatures[i] >= temperatures[s.top()]) {//当看到前面都是更低的温度,出栈
                s.pop();
            }
            s.empty()? ans[i] = 0:ans[i] = s.top() - i;	
            s.push(i);
        }
        return ans;
    }
};

复杂度分析

同上

关于单调栈

一开始对递增/递减单调栈区别错误了,以为区别就是正反向遍历。

  • 单调递增栈:从栈头到栈底数据是从大到小(栈头最大)
  • 单调递减栈:从栈头到栈底数据是从小到大(栈头最小)

上面两个单调栈解法,都属于单调递减栈。

关于评论区的精彩解答:

那有同学就问了,我怎么能想到用单调栈呢? 什么时候用单调栈呢?

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了

单调栈的本质用空间换时间。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种特殊的栈结构,它可以快速找到栈中的最大值或最小值。单调栈常用于解决一些数组、字符串中的问题,例如求下一个更大的元素、求前一个更大的元素等等。 下面是单调栈的代码模板: ``` stack<int> mono_stack; for (int i = 0; i < n; i++) { while (!mono_stack.empty() && mono_stack.top() > x[i]) { mono_stack.pop(); } mono_stack.push(x[i]); } ``` 其中,`x` 是一个数组,`n` 是数组的长度,`mono_stack` 是单调栈。这段代码的意思是:遍历数组 `x`,对于每个元素 `x[i]`,将其压入栈中,然后不断弹出栈顶元素,直到栈为空或者栈顶元素不大于 `x[i]`。这样,最终单调栈中的元素就是一个单调递增的序列。 如果要求下一个更大的元素,可以在遍历数组时,记录每个元素的下标,然后在弹出栈顶元素时,更新下标对应的答案。代码如下: ``` stack<pair<int, int>> mono_stack; for (int i = 0; i < n; i++) { while (!mono_stack.empty() && mono_stack.top().first < x[i]) { ans[mono_stack.top().second] = x[i]; mono_stack.pop(); } mono_stack.push(make_pair(x[i], i)); } ``` 其中,`ans` 是答案数组,`ans[i]` 表示第 `i` 个元素的下一个更大的元素。在弹出栈顶元素时,更新对应下标的答案即可。 如果要求前一个更大的元素,可以将数组逆序遍历,然后按照同样的方式求解。代码如下: ``` stack<pair<int, int>> mono_stack; for (int i = n - 1; i >= 0; i--) { while (!mono_stack.empty() && mono_stack.top().first < x[i]) { ans[mono_stack.top().second] = x[i]; mono_stack.pop(); } mono_stack.push(make_pair(x[i], i)); } ``` 以上就是单调栈的代码模板,可以根据具体问题进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值