面试必刷算法TOP101之单调栈问题 TOP 23

每日天气

题目来源:leetcode

1、问题描述

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

2、思路解析

思路一:循环暴力破解
定义一个ans数组 用来存放结果,两层for循环。外层for循环用来遍历数组的每一个元素,内层循环寻找第i天之后第一个出现比当前天气高的数组的下标,当寻找到第一个比当前值高的结果时,就会立即保存结果,并跳出循环,将结果插入到ans数组。由于采用的是双for循环内层循环最好情况是循环一次,所以时间复杂度为O(n*m)。
思路二: 单调栈
思路就是将一个一个元素入栈,定义一个栈s,这不像其他栈一样,他是一个单调栈,所谓单调栈就是从栈顶到栈地数据是递增的但不是像其他栈那样直接入栈,而是又顺序入栈,栈中元素是递减的或者递增的,因为题意要知道当前之后,多少天之后气温会比当前气温高,因为下标+1就是对应的天数,所以将对应的数组下标存入到单调栈中,其所对应的值不是递增就是递减的。for循环从1开始遍历元素,当遇到栈顶元素比当前元素小因为要保证栈中元素是有序的,所以将栈中元素小于当前元素都出栈,同时出栈元素也是数组下标,将当先下标减去栈顶元素值存入到ans[s.top]中,对应的就是第s.top()天之后多少天会有升温情况.
为什么使用单调栈可以找到元素向左遍历第一个比他大的元素,而不是最后一个比他大的元素呢?

先来看一个例子:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
由于单调栈中的元素只能是单调递增或者是单调递减的,所以可以分为这两种情况:
1.当单调栈中的元素是递增的时候,根据上面从数组的角度可以得出:
(1)当a > s.top() 时,则将元素a插入栈顶,新的栈顶则为a
(2)当a < s.top()时,则将从当前栈顶位置向前查找(边查找,栈顶元素边出栈),直到找到第一个比a小的数,停止查找,将元素a插入栈顶(在当前找到的数之后,即此时元素a找到了自己的位置)
2.当单调栈中的元素是递减的时候,则有:
(1)当a < b 时,则将元素a插入栈顶,新的栈顶则为a
(2)当a > b 时,则将从当前栈顶位置向前查找(边查找,栈顶元素边出栈),直到找到第一个比a大的数,停止查找,将元素a
插入栈顶(在当前找到的数之后,即此时元素a找到了自己的位置)

3、代码实现

class Solution {
public:
    //暴力破解
    vector<int> dailyTemperatures(vector<int>& temp) {
        vector<int> v;
        for(int i=0;i<temp.size();i++){
            int num=0;
            for(int j=i+1;j<temp.size();j++){
                if(temp[i]<temp[j]){
                    num=j-i;
                    break;
                }
            }
            v.push_back(num);

        }
        return v;

    }
};
class Solution {
public:
    //暴力破解
    vector<int> dailyTemperatures(vector<int>& temp) {
        
        vector<int>v(temp.size(),0);
        stack<int> s;
        //先将第一个元素入栈
        s.push(0);
        for(int i=1;i<temp.size();i++){
            while(!s.empty()&&temp[i]>temp[s.top()]){
                //栈顶元素比新来元素小
                v[s.top()]=i-s.top();
                s.pop();
            }
            //新来元素小于栈顶元素直接下标入栈
            s.push(i);
        }
        return v;

    }
};

看起来时间复杂度像O(n*m),但是只遍历一边元素,所以时间复杂度为O(N).

下一个更大元素 I

问题来源:leetcode

1、问题描述

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

2、思路解析

思路一:循环暴力破解
双for循环,外层循环遍历数组nums1每一个元素,然后对于每一个元素,用find找到对应nums2中的位置,然后内存循环从找到的下一个位置开始遍历,找到第一个大于nums[i]的元素就保存下来,没有找到就将-1存入到数组中,直到遍历完数组。因为两个数组的长度可能不相等,所以时间复杂度为O(n*m).

思路二:哈希+单调栈
先定义一个map,遍历数组nums1,将nums1中元素作为map的键值,下标作为values,这是为了后边做准备。后边就像上边每日天气算法相似,将0先入栈,从1开始遍历元素,内部while循环遍历单调栈,当新来元素比栈顶元素大,栈顶元素就出栈,同时利用map的count函数判断,栈顶元素在nums1中有没有存在,如果存在那么新来元素就是栈顶元素对应的右边第一个比栈顶元素大的数据,就将他保存在ans数组中。

3、代码实现

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
     
        vector<int> v;
        for(int i=0;i<nums1.size();i++){ 
            stack<int> s;
            int num=-1;
            //首先找到要找元素位置
           int j=find(nums2.begin(),nums2.end(),nums1[i])-nums2.begin()+1;
           s.push(nums1[i]);
           //在nums2后续第一个比nums1[i]大的元素
            for(;j<nums2.size();j++){

               if(nums2[j]>nums1[i]){
                   num=nums2[j];
                   break;
               }

            }
            //将找到的数组存放在ans数组中
            v.push_back(num);
        }
        return v;

    }
};
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
     unordered_map<int,int>mp;
     //将数组中的元素存放到map中充当键值,下标充当values
     for(int i=0;i<nums1.size();i++){
         mp[nums1[i]]=i;
     }
     stack<int> s;

     vector<int> v(nums1.size(),-1);
     s.push(0);
     for(int i=1;i<nums2.size();i++){
         while(!s.empty()&&nums2[i]>nums2[s.top()]){
             //寻找对顶元素是不是在nums1中存在
             if(mp.count(nums2[s.top()])){
                 //存在将右边第一个比他的大的元素存放在ans数组中
                 v[mp[nums2[s.top()]]]=nums2[i];
             }
             s.pop();
         }
         //新来元素入栈
         s.push(i);
     }
     return v;
    }
};
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自首的小偷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值