代码随想录算法训练营29期|day62 任务以及具体安排

文章讲述了如何使用单调栈优化算法解决每日温度问题和下一个更大元素问题,通过递增栈记录元素及其右侧第一个更大元素的位置,以及如何在`dailyTemperatures`和`nextGreaterElement`函数中实现这一思想。
摘要由CSDN通过智能技术生成

章 单调栈part01

  •  739. 每日温度 
    class Solution {
      // 版本 1
        public int[] dailyTemperatures(int[] temperatures) {
    
            int lens=temperatures.length;
            int []res=new int[lens];
    
            /**
            如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,
            	所以弹出 栈顶元素,并记录
            	如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系
            否则的话,可以直接入栈。
            注意,单调栈里 加入的元素是 下标。
            */
            Deque<Integer> stack=new LinkedList<>();
            stack.push(0);
            for(int i=1;i<lens;i++){
    
                if(temperatures[i]<=temperatures[stack.peek()]){
                    stack.push(i);
                }else{
                    while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
                        res[stack.peek()]=i-stack.peek();
                        stack.pop();
                    }
                    stack.push(i);
                }
            }
    
            return  res;
        }

    思路:单调栈的应用,单调栈解决左面或右面第一个大或小的元素,其实就是自己模拟单调栈。

    思路

    首先想到的当然是暴力解法,两层for循环,把至少需要等待的天数就搜出来了。时间复杂度是O(n^2)

    那么接下来在来看看使用单调栈的解法。

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

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

    例如本题其实就是找找到一个元素右边第一个比自己大的元素,此时就应该想到用单调栈了。

    那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?

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

    更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。

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

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

  • 单调栈里元素是递增呢? 还是递减呢?
  • 注意以下讲解中,顺序的描述为 从栈头到栈底的顺序,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定比较懵。

    这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,栈里要加入一个元素i的时候,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。

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

    文字描述理解起来有点费劲,接下来我画了一系列的图,来讲解单调栈的工作过程,大家再去思考,本题为什么是递增栈。

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

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
  • 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
  • 通过以上过程,大家可以自己再模拟一遍,就会发现:只有单调栈递增(从栈口到栈底顺序),就是求右边第一个比自己大的,单调栈递减的话,就是求右边第一个比自己小的。

    把这三种情况分析清楚了,也就理解透彻了

    接下来我们用temperatures = [73, 74, 75, 71, 71, 72, 76, 73]为例来逐步分析,输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。


    首先先将第一个遍历元素加入单调栈

    739.每日温度1


    加入T[1] = 74,因为T[1] > T[0](当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况)。

    我们要保持一个递增单调栈(从栈头到栈底),所以将T[0]弹出,T[1]加入,此时result数组可以记录了,result[0] = 1,即T[0]右面第一个比T[0]大的元素是T[1]。

    739.每日温度2


    加入T[2],同理,T[1]弹出

    739.每日温度3


    加入T[3],T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),加T[3]加入单调栈。

    739.每日温度4


    加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于!

    739.每日温度5


    加入T[5],T[5] > T[4] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[4]弹出,同时计算距离,更新result 

    739.每日温度6


    T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[3]继续弹出,同时计算距离,更新result 

    739.每日温度7


    直到发现T[5]小于T[st.top()],终止弹出,将T[5]加入单调栈

    739.每日温度8


    加入T[6],同理,需要将栈里的T[5],T[2]弹出

    739.每日温度9


    同理,继续弹出

    739.每日温度10


    此时栈里只剩下了T[6]

    739.每日温度11


    加入T[7], T[7] < T[6] 直接入栈,这就是最后的情况,result数组也更新完了。

    739.每日温度12

    此时有同学可能就疑惑了,那result[6] , result[7]怎么没更新啊,元素也一直在栈里。

    其实定义result数组的时候,就应该直接初始化为0,如果result没有更新,说明这个元素右面没有更大的了,也就是为0。

    以上在图解的时候,已经把,这三种情况都做了详细的分析。

  •  496.下一个更大元素 I  
    class Solution {
        public int[] nextGreaterElement(int[] nums1, int[] nums2) {
            Stack<Integer> temp = new Stack<>();
            int[] res = new int[nums1.length];
            Arrays.fill(res,-1);
            HashMap<Integer, Integer> hashMap = new HashMap<>();
            for (int i = 0 ; i< nums1.length ; i++){
                hashMap.put(nums1[i],i);
            }
            temp.add(0);
            for (int i = 1; i < nums2.length; i++) {
                if (nums2[i] <= nums2[temp.peek()]) {
                    temp.add(i);
                } else {
                    while (!temp.isEmpty() && nums2[temp.peek()] < nums2[i]) {
                        if (hashMap.containsKey(nums2[temp.peek()])){
                            Integer index = hashMap.get(nums2[temp.peek()]);
                            res[index] = nums2[i];
                        }
                        temp.pop();
                    }
                    temp.add(i);
                }
            }
    
            return res;
        }
    }

    思路:该题也是典型的单调栈的应用,主要是换了一个壳子,求nums2的下一个较大数字,然后将num1存在map里,当找到一个更大值时,查找在map里面是否出现,然后赋值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值