给定一个整数数组
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
对于温度列表中的每个元素 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 的最小下标。
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。
空间换时间
对于本题而言, 因为在遍历过程中,需要使用一个栈记录右边第一个比当前元素大的元素的元素,仅仅只需要遍历一次
在使用单调栈的过程中,需要明确以下几点:
单调栈里存放的元素是什么?
存储元素的下标即可,如果需要获取对应的元素, 直接T[i]即可获取
单调栈里的元素是递增还是递减?
顺序:栈顶->栈底
本题使用的递增,因为只有递增时,加入下一个元素i,才知道栈顶元素在数组中右边第一个比栈顶元素大的元素是i
使用单调栈主要有三个判断条件:
举个栗子来说明这三种情况:
temperatures = [73, 74, 75, 71, 71, 72, 76, 73] 输出结果: [1, 1, 4, 2, 1, 1, 0, 0]。
加入T[1] = 74, 此时T[1] > T[0](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[0]弹出,T[1]加入,此时result数组记录result[0] = 1,即T[0]右边第一个比T[0]大的元素是T[1]。
加入T[2]=75,此时T[2] > T[1](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[1]弹出,T[2]加入,此时result数组记录result[1] = 1,即T[1]右边第一个比T[1]大的元素是T[2]。
加入T[4]=71,此时T[4]=T[3](情况2),由于要保持单调栈的性质(栈顶->栈底),T[3]加入单调栈,此时不用计算距离
加入T[5]=72,此时T[5]>T[4](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[4]弹出,T[5]加入,此时result数组记录result[4] = 5-4=1,即T[4]右边第一个比T[4]大的元素是T[5]。
当T[4]弹出后, 此时T[5]>T[3](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[3]弹出,T[5]加入,此时result数组记录result[3] = 5-3=2,即T[3]右边第一个比T[3]大的元素是T[5]。
加入T[6],此时T[6]>T[5](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[5]弹出,T[6]加入,此时result数组记录result[5] = 6-5=1,即T[5]右边第一个比T[5]大的元素是T[6]。
当T[5]弹出后,此时T[6]>T[2](情况3),由于要保持单调栈的性质(栈顶->栈底),故T[2]弹出,T[6]加入,此时result数组记录result[2] = 6-2=4,即T[2]右边第一个比T[2]大的元素是T[6]。
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 记录每个温度第一次出现的下标位置。
# 方式一
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 是温度列表的长度。需要维护一个单调栈存储温度列表中的下标。