代码随想录算法训练营第四十七天 | 739.每日温度 496.下一个更大元素Ⅰ 503.下一个更大元素Ⅱ

LeetCode 739.每日温度:

文章链接
题目链接:739.每日温度

思路:

当题目需要用到元素左边或者右边第一个更大/更小元素时,需要用到单调栈。而单调栈,顾名思义,是一个栈且栈中元素(从栈顶到栈底)是有序的。
以需要得到元素右边第一个更大元素为例,栈中保持元素,

  • 如果新加的元素 i > 栈顶元素,那么出栈栈顶元素,并进行处理,而出栈一个元素后的栈定元素如果还是 < 新元素 i,那么继续出栈,直到栈空或者新加的元素 i < 栈顶元素,再将 i 入栈
  • 如果新加的元素 i < 栈顶元素,那么将新加的元素 i 入栈
  • 相等的情况,要看题目是要求更大的还是说可以相等,要求更大的话,i 直接入栈,可以相等的话,那么按照 i > 栈顶元素的处理方式处理
    这样处理下来,新加入栈的元素 i 一定比之前的栈顶元素 小,因而从栈顶往栈底看,元素是递增的,从而这个栈是单调栈。

回到本题,分析题目可知,要求的是元素右边第一个更大的元素,不包括等于,而answer中保存的是间隔多少天,因此栈中保存数组下标。单调栈问题要分下面三种情况

  • T[i] > T[stack[-1]]:即大于栈顶元素,那么当栈非空时,只要满足这个条件,就一直出栈元素,并且赋值answer[stack[-1]] = i - stack[-1]。最后入栈 i
  • T[i] = T[stack[-1]]:等于栈顶元素,直接入栈
  • T[i] < T[stack[-1]]:小于栈顶元素,直接入栈
    同时answer初始化为0,那么遍历到最后,如果栈中还有元素,根据题目定义,对应的answer的值为0,初始化为0从而不用对最后栈中还剩下的元素进行处理
class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        lent = len(temperatures)
        if lent == 1:
            return [0]
        if lent == 0:
            return 0
        stack = [0] # 栈中保存的是下标
        result = [0] * lent
        for i in range(1, lent):
            if temperatures[i] < temperatures[stack[-1]]:
                stack.append(i)
            elif temperatures[i] == temperatures[stack[-1]]:
                stack.append(i)
            else:
                while len(stack) != 0 and temperatures[i] > temperatures[stack[-1]]:
                    j = stack.pop()
                    result[j] = i - j
                stack.append(i)
        return result

简化版

"""
也可以加一个变量top指向栈顶,从而判断栈空时直接用top
"""
class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        lent = len(temperatures)
        if lent == 1:
            return [0]
        if lent == 0:
            return 0
        stack = [0] # 栈中保存的是下标
        result = [0] * lent
        for i in range(1, lent):
            while len(stack) != 0 and temperatures[i] > temperatures[stack[-1]]:
                j = stack.pop()
                result[j] = i - j
            stack.append(i)
        return result

LeetCode 496.下一个更大元素Ⅰ:

文章链接
题目链接:496.下一个更大元素Ⅰ

思路

分析题目:要求的是nums1中元素x在x在nums2对应位置的右侧第一个比x大的元素,如果不存在返回-1,nums1是nums2的子集。最后返回nums1长度的数组作为答案
本题在上面题目的基础上有所改编,遍历求右侧第一个更大元素的是nums2,然后从nums2的所有元素的右侧第一个更大元素的数组中,取出nums1数组中对应的值。
那么遍历nums2,使用单调栈求右侧第一个更大元素,当栈顶元素是nums1的元素时,给result赋值,而nums1和nums2都没有重复元素,因此首先需要将nums1的元素值与下标做映射,也就是能根据元素值得到对应的下标

  • 使用字典建立nums1中数值到下标的映射
for i in range(len1):
    ndict[nums1[i]] = i
  • result初始化为-1,因为题目中没有找到就是-1
  • 然后遍历nums2,使用单调栈求右侧第一个更大的元素
    • num > top:出栈栈顶元素,如果栈顶元素是nums1中元素,给result数组赋值。然后当栈非空且num > top时一直循环上面的过程。循环结束后要记得将num加入栈中
    • num = top,将num加入栈中
    • num < top,将num加入栈中
      代码
class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        len1 = len(nums1)
        ndict = dict()
        # 建立nums1的数值和下标的映射
        for i in range(len1):
            ndict[nums1[i]] = i
        # 使用单调栈求右边第一个更大的
        result = [-1] * len1
        stack = [nums2[0]]  # 栈中存储元素的值
        for num in nums2[1:]:   # 求的是nums2中右边更大的
            if num < stack[-1]:
                stack.append(num)
            elif num == stack[-1]:
                stack.append(num)
            else:
                while len(stack) != 0 and num > stack[-1]:
                    topstack = stack.pop()
                    if topstack in ndict:   # 栈顶元素在nums1中
                        result[ndict[topstack]] = num
                stack.append(num)   # 这个喜欢忘记
        return result

        

LeetCode 503.下一个更大元素Ⅱ:

文章链接
题目链接:503.下一个更大元素Ⅱ

思路:

本题还是单调栈问题,但是变成了循环数组。

  • 单调栈需要注意的是:
    • ① 要求的是左边 / 右边,第一个更大的还是更小的
    • ② 要返回的数组result中,如果找到目标值了,result中保存的应该是目标值 / 目标值的下标 / 目标值下标与栈顶元素下标的差值;没找到目标值,result保存的是 0 / -1或其它值
    • ③ 栈中保存的是下标还是值,一般是下标,如果nums数组是不重复的数组,那么保存值也可以
  • 循环数组:解决办法就是遍历两遍nums数组即可,
    • 要么将两个nums数组拼成新数组,然后遍历新数组得到result,在resize到nums的大小,但是其中拼接过程相当于多了一个O(n)的操作。
    • 要么不拼接nums得到新数组,而是模拟遍历两次nums的情况,其中i ∈ [1, lenn * 2 - 1](双闭),但是因为nums没扩充,所以 i 与nums下标的对应关系为 j = i % lenn
      代码
class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        lenn = len(nums)
        result = [-1] * lenn
        if lenn <= 1:
            return result
        # 单调栈,栈保存下标
        stack = [0]
        # 模拟两遍遍历nums
        for i in range(1, lenn * 2):
            if nums[i % lenn] < nums[stack[-1]]:    # 注意这里是 i % lenn
                stack.append(i % lenn)
            elif nums[i % lenn] == nums[stack[-1]]:
                stack.append(i % lenn)
            else:
                while len(stack) > 0 and nums[i % lenn] > nums[stack[-1]]:
                    index = stack.pop()
                    result[index] = nums[i % lenn]
                stack.append(i % lenn)  # 容易忘记
        return result 
        

学习收获:

理解单调栈是什么、怎么用。
单调栈使用过程中要注意的东西:

  • 找到目标值后保存的是什么、没找到目标值后保存的是什么;
  • 要求的是左/右,下一个更大/更小;
  • 栈中保存什么;
  • 以及三种情况 >, = , < 时的处理
    每日温度:单调栈
    下一个更大元素Ⅰ:nums1在nums2找右侧下一个更大元素,需要实现nums1的数值和下标的映射,以及num > top时,如果top是nums1中的元素才给result对应下标赋值
    下一个更大元素Ⅱ:每日温度的变更,变成了循环数组,实现两次遍历该数组即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值