Leetcoder Day43| 单调栈1

739. 每日温度

请根据每日气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

本题最直接的思路就是挨个遍历,设置两个指针,一个 i 指向当前元素,一个 j 从当前元素后一个元素开始遍历,当遇到第一个大于当前元素的值就返回j-i。

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int lens=temperatures.length;
        int[] answer= new int[lens];  //初始化默认为0
        for(int i=0;i<lens-1;i++){
            for(int j=i+1;j<lens;j++){
                if(temperatures[j]>temperatures[i]){
                    answer[i]=j-i;
                    break;
                }
            }
        }
        answer[lens-1]=0;
        return answer;
    }
}

但是这个会在某一个测试用例中不通过因为需要用到两次for循环,时间复杂度很高。

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

单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。

在使用单调栈的时候首先要明确如下几点:

  1. 单调栈里存放的元素是什么?
  2. 单调栈里元素是递增呢? 还是递减呢?

单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。

 从栈顶到栈底的顺序需要递增的,因为这时在栈里要加入一个元素 i 才表示:在数组中右边第一个比栈顶元素大的是i。

  • 如果求一个元素右边第一个更大元素,单调栈就是递增的;
  • 如果求一个元素右边第一个更小元素,单调栈就是递减的。

使用单调栈主要有三个判断条件。

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

因为要找的是右边第一个更大的元素,所以当前遍历到的元素,如果小于或等于栈顶元素,都会被压入到栈中,如果大于栈顶元素,才会将距离结果计算并返回到answer中,下面是具体思路:

  1. 首先将第一个元素的下标压入栈中
  2. 其次将栈顶元素对应的值与第二个元素的值进行对比,如果第二个元素的值大于栈顶元素值,则将栈顶元素的下标从栈中弹出,并返回下标的差值。如果小于或者等于栈顶元素的值,则将第二个元素下标也压入栈中;
  3. 继续判断后面的元素的值和栈顶元素对应的值的关系。若遇到比栈顶元素大的元素,则将栈顶元素弹出,计算下标的差值并返回给answer[栈顶元素值]
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int lens=temperatures.length;
        int []answer=new int[lens];
        Deque<Integer> stack=new LinkedList<>();
        stack.push(0);
        for(int i=1;i<lens;i++){
            while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
                answer[stack.peek()]=i-stack.peek();
                stack.pop();
            }
            stack.push(i);
        }
        return answer;
    }
}

496.下一个更大元素 I

给你两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例 2:
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出-1 。

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 10^4
  • nums1和nums2中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2 中

本题的意思是:有一长一短两个数组,短数组中的元素一定在长数组中出现过,但是两个数组的元素顺序不同,定义一个结果数组res,遍历短数组中的每一个元素,找出当前元素的值在长数组中的位置,在长数组的顺序中找出右边第一个更大的元素并返回这个元素的值,若右边没有更大的元素则返回-1。因此和每日温度的解题思路很类似,依然是利用从栈顶到栈尾递增到单调栈。只不过这里结果返回的是元素并且还有一个难点是如何找到当前短数组元素在长数组中的位置。

如何找到长数组中短数组元素的位置呢

一般要对应两个数组的位置,首先考虑到用map来进行映射,key存放到是元素值,value存放下标。那么这里是存放短还是长数组呢,因为我们需要在长数组中找顺序,所以其实需要遍历整个长数组,因为map存短数组的值和下标即可,在遍历长数组时,根据key判断当前元素是否在短数组中存在,如果存在,则开始判断,并且将第一个满足条件的值存入ans[当前元素在短数组的下标]中。如果不存在,则遍历到下一个元素继续进行判断。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int lens=nums1.length;
        int[] ans=new int[lens];
        Arrays.fill(ans, -1);  //将数组初始值设置为-1
        Deque<Integer> stack =new LinkedList<>();
        HashMap<Integer, Integer> map=new HashMap<>();
        //利用map将nums1的值和下标存入
        for(int i=0; i<lens;i++){
            map.put(nums1[i], i);
        }
        stack.push(0);
        for(int i=1;i<nums2.length;i++){
            if(!map.containsKey(nums2[stack.peek()])){
                stack.pop();
            }else{
                while(!stack.isEmpty() && nums2[i]>nums2[stack.peek()]){//栈不为空,且当前元素大于栈顶元素,弹出栈顶
                    int idx=map.get(nums2[stack.peek()]) ;//获取栈顶下标对应在nums2中的值在nums1的下标
                    ans[idx]=nums2[i];// 当前nums2中的元素为第一个右边更大元素
                    stack.pop();
                }
            }
            stack.push(i);
        }
        return ans;

    }
}

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值