题目描述:编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。
今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/online-stock-span
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
本题仍然使用从栈底到栈顶(从大到小)顺序的单调栈来维护各个价格之间的顺序。
在类的init
方法内准备一个单调栈和一个price
数组。next
方法的开始和普通的单调栈用法一致,如果不满足条件就退栈,当满足条件时就把当前元素进栈。
在这里,我们重点讲解的是如何判断连续的价格低于当日价格的天数。如下图,我们假定有三个位置i
、j
、k
,对应的元素为nums[i]
、nums[j]
、nums[k]
(下边描述的nums[k],nums[j]进栈只是为了直观的展示元素之间大小,代码中进栈出栈的是元素索引)。
- 如果
k
位置元素进栈,使得整个栈为空,说明k
位置之前的元素都小于等于k
位置元素。
反证:假定在[0:k-1]
范围上存在一个位置j
,使得nums[j] >=nums[k]
,那么当nums[k]
进栈时,nums[j]
一定会被留在栈内,但事实是栈内没有元素,所以可得矛盾 - 如果
k
位置元素进栈,没有元素出栈,说明栈内元素都大于k
位置元素,结果也就是1 - 如果
k
位置元素进栈,造成大量元素出栈但是栈并没有为空,我们假定现在栈顶索引为i
,那么就可以确定[i:k]
范围里的全部元素都一定小于等于nums[k]
,结果就为k
位置减去栈顶元素位置。
反证:假定在[i:k-1]
范围内存在一个位置j,使得nums[j] > nums[k]
,那么当nums[k]
进栈时,退栈到j
位置就要结束了,因为nums[j]>nums[k]
,但事实并非如此,与假定情况矛盾
class StockSpanner:
def __init__(self):
# 单调栈
self.mon_stack = list()
# 单调栈记录索引(日期),prices记录所有的价格
self.prices = list()
def next(self, price: int) -> int:
self.prices.append(price)
while self.mon_stack and self.prices[self.mon_stack[-1]] <= price:
self.mon_stack.pop()
self.mon_stack.append(len(self.prices)-1)
# 如果除了刚进栈的元素,其他元素都出栈了
# 说明当前索引i之前的元素都小于它
if len(self.mon_stack) == 1:
return len(self.prices)
else:
return len(self.prices) - self.mon_stack[-2] - 1