Leetcode 739. 每日温度

1.题目描述

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


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


输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]


输入: temperatures = [30,60,90]
输出: [1,1,0]


提示:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

2. 思路分析

2.1 暴力解法

对于温度列表中的每个元素 temperatures[i],需要找到最小的下标 j,使得 i < j 且 temperatures[i]

< temperatures[j]。

由于温度范围在 [30, 100] 之内,因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组

next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。

反向遍历温度列表。对于每个元素 temperatures[i],在数组 next 中找到从 temperatures[i] + 1 到

100 中每个温度第一次出现的下标,将其中的最小下标记为 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 的最小下标。

2.2 单调栈

2.2.1 什么时候使用单调栈?

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置

2.2.2 单调栈的本质是什么?

空间换时间


对于本题而言, 因为在遍历过程中,需要使用一个栈记录右边第一个比当前元素大的元素的元素,仅仅只需要遍历一次

在使用单调栈的过程中,需要明确以下几点:

  1. 单调栈里存放的元素是什么?

    存储元素的下标即可,如果需要获取对应的元素, 直接T[i]即可获取

  2. 单调栈里的元素是递增还是递减?

    顺序:栈顶->栈底

    本题使用的递增,因为只有递增时,加入下一个元素i,才知道栈顶元素在数组中右边第一个比栈顶元素大的元素是i

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

  • 情况1:当前遍历元素T[i] < 栈顶元素 T[st.top()]的情况
  • 情况2:当前遍历元素T[i] == 栈顶元素 T[st.top()]的情况
  • 情况3:当前遍历元素T[i] > 栈顶元素 T[st.top()]的情况

举个栗子来说明这三种情况:

temperatures = [73, 74, 75, 71, 71, 72, 76, 73] 输出结果: [1, 1, 4, 2, 1, 1, 0, 0]。

  1. 将第一个遍历元素加入单调栈

Leetcode 739. 每日温度_第1张图片

  1. 加入T[1] = 74, 此时T[1] > T[0](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[0]弹出,T[1]加入,此时result数组记录result[0] = 1,即T[0]右边第一个比T[0]大的元素是T[1]。
    Leetcode 739. 每日温度_第2张图片

  2. 加入T[2]=75,此时T[2] > T[1](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[1]弹出,T[2]加入,此时result数组记录result[1] = 1,即T[1]右边第一个比T[1]大的元素是T[2]。
    Leetcode 739. 每日温度_第3张图片

  3. 加入T[3]=71,此时T[3]栈底),T[3]加入单调栈,此时不用计算距离
    Leetcode 739. 每日温度_第4张图片

  4. 加入T[4]=71,此时T[4]=T[3](情况2),由于要保持单调栈的性质(栈顶->栈底),T[3]加入单调栈,此时不用计算距离
    Leetcode 739. 每日温度_第5张图片

  5. 加入T[5]=72,此时T[5]>T[4](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[4]弹出,T[5]加入,此时result数组记录result[4] = 5-4=1,即T[4]右边第一个比T[4]大的元素是T[5]。
    Leetcode 739. 每日温度_第6张图片

  6. 当T[4]弹出后, 此时T[5]>T[3](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[3]弹出,T[5]加入,此时result数组记录result[3] = 5-3=2,即T[3]右边第一个比T[3]大的元素是T[5]。
    Leetcode 739. 每日温度_第7张图片

  7. 加入T[6],此时T[6]>T[5](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[5]弹出,T[6]加入,此时result数组记录result[5] = 6-5=1,即T[5]右边第一个比T[5]大的元素是T[6]。
    Leetcode 739. 每日温度_第8张图片

  8. 当T[5]弹出后,此时T[6]>T[2](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[2]弹出,T[6]加入,此时result数组记录result[2] = 6-2=4,即T[2]右边第一个比T[2]大的元素是T[6]。
    Leetcode 739. 每日温度_第9张图片

  9. 此时栈里只剩下T[6], 加入T[7]=73, 此时T[7]栈底),故T[7]入栈,此时不用计算距离。
    Leetcode 739. 每日温度_第10张图片

3.代码实现

3.1 暴力解法

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        # 定义保存结果的ans数组、每个元素第一次出现的位置
        ans, next, big = [0] * n, dict(), 10**9
        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

复杂度分析

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

3.2 单调栈

# 方式一
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
# 方式二
class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        length = len(temperatures)
        ans = [0] * length
        stack = []
        for i in range(length):
            # 情况3
            while stack and temperatures[i] > temperatures[stack[-1]]:
                ans[stack[-1]] = i - stack[-1]
                stack.pop()
            # 情况1和情况2
            stack.append(i)
        return ans

复杂度分析

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

你可能感兴趣的:(数据结构,Leetcode,leetcode,算法,数据结构)