【单调栈】力扣739:每日温度()

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

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

方法1:暴力

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        answer = [0] * n
        for i in range(n - 1):
            for j in range(i + 1, n):
                if temperatures[j] > temperatures[i]:
                    answer[i] = j - i
                    break
        return answer

时间复杂度O(^2),超出时间限制

方法2:暴力栈
对于温度列表中的每个元素 temperatures[i],需要找到最小的下标 j,使得 i < j temperatures[i] < temperatures[j]。
由于温度范围为 [30, 100],因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组 next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。
反向遍历温度列表。对于每个元素 temperatures[i],在数组 next 中找到从 temperatures[i] + 1 到 100+1=101 中每个温度第一次出现的下标,将其中的最小下标记为 warmerIndex,则 warmerIndex 为下一次温度比当天高的下标。如果 warmerIndex 不为无穷大,则 warmerIndex - i 即为下一次温度比当天高的等待天数,最后令 next[temperatures[i]] = i。
为什么上述做法可以保证正确呢?因为遍历温度列表的方向是反向,当遍历到元素 temperatures[i] 时,只有 temperatures[i] 后面的元素被访问过,即对于任意 t,当 next[t] 不为无穷大时,一定存在 j 使得 temperatures[j] == t 且 i < j。又由于遍历到温度列表中的每个元素时都会更新数组 next 中的对应温度的元素值,因此对于任意 t,当 next[t] 不为无穷大时,令 j = next[t],则 j 是满足 temperatures[j] == t 且 i < j 的最小下标。

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        ans, next, big = [0] * n, dict(), 100000
        for i in range(n - 1, -1, -1):
            warmer_index = min(next.get(t, big) for t in range(temperatures[i] + 1, 102))
            if warmer_index != big:
                ans[i] = warmer_index - i
            next[temperatures[i]] = i
        return ans

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/daily-temperatures/solution/mei-ri-wen-du-by-leetcode-solution/

时间复杂度:O(nm),其中 nn 是温度列表的长度,m 是数组 next 的长度,在本题中温度不超过 100,所以 m 的值为 100。反向遍历温度列表一遍,对于温度列表中的每个值,都要遍历数组 next 一遍。
空间复杂度:O(m),其中 m 是数组 next 的长度。除了返回值以外,需要维护长度为 m 的数组 next 记录每个温度第一次出现的下标位置。

方法3:单调栈
1. 怎么能想到用单调栈呢?什么时候用单调栈呢?
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时就要想到可以用单调栈了。时间复杂度为O(n)。
例如本题其实就是找到一个元素右边第一个比自己大的元素。
2. 单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素大的元素,优点是只需要遍历一次。
在使用单调栈的时候要明确如下几点:

  • 单调栈里存放的元素是什么?
    单调栈里只需要存放元素的下标 i 就可以了,如果需要使用对应的元素,直接在 T[i] 就可以获取。
  • 单调栈里的元素是递增还是递减呢?
    注意一下顺序为从栈顶到栈底,因为单纯说从左到右或者从前到后,不说栈头朝哪个方向的话,一定会懵。
    这里要使用递增循序(从栈头到栈底递增),因为只有递增的时候,加入一个元素 i ,才知道栈顶元素在数组中右边第一个比栈顶元素大的元素是i。

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

  • 当前遍历的元素 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]。

  1. 先将第一个遍历元素T[0] = 73加入单调栈,下标为0
    image
  2. 加入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]。
    image
  3. 加入T[2] = 75,同理,T[1]弹出
    image
    (https://img2022.cnblogs.com/blog/1963371/202205/1963371-20220522162712906-1661237473.png)
  4. 加入T[3] = 71,T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),将T[3]加入单调栈。
    image
  5. 加入T[4] = 71,T[4] == T[3](当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为要求的是右面第一个大于本元素的位置,而不是大于等于!
    image
  6. 加入T[5] = 72,T[5] > T[4](当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[4]弹出,同时计算距离,更新result。T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[3]继续弹出,同时计算距离,更新result。直到发现T[5]小于T[st.top()],终止弹出,将T[5]加入单调栈。
    image
    image
    image
    image
  7. 加入T[6] = 76,同理,需要将栈里的T[5]、T[2]弹出。此时栈里只剩下T[6]。
    image
  8. 加入T[7] = 72, T[7] < T[6] 直接入栈,这就是最后的情况,result数组也更新完了。
    image

思考:result[6], result[7]怎么没更新呢?元素也一直在栈里。
其实定义result数组的时候,就应该直接初始化为 0,如果result没有更新,说明这个元素右面没有更大的了,也就是为 0。

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        answer = [0]*len(temperatures)
        stack = [0]
        for i in range(1,len(temperatures)):
            # 情况一和情况二
            if temperatures[i]<=temperatures[stack[-1]]:
                stack.append(i)
            # 情况三
            else:
                while len(stack) != 0 and temperatures[i]>temperatures[stack[-1]]:
                    answer[stack[-1]]=i-stack[-1]
                    stack.pop()
                stack.append(i)
        return answer

作者:carlsun-2
链接:https://leetcode.cn/problems/daily-temperatures/solution/dai-ma-sui-xiang-lu-dai-ni-xue-tou-dan-d-kwbv/

简单版:

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        stack = []
        # 初始化全用0,对于没找到比其大的全为0
        res = [0] * len(temperatures)
        for i, temp in enumerate(temperatures):
            while stack and temp > stack[-1][1]:
                idx = stack.pop()[0]
                res[idx] = i - idx

             # i 用于记录所在位置和计算天数, temp 温度值作为递增判断
            stack.append((i, temp))
        return res

作者:Jam007
链接:https://leetcode.cn/problems/daily-temperatures/solution/si-lu-jian-dan-xing-neng-gao-xiao-by-jam-eiov/

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

posted @   Vonos  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示