股票价格跨度

标签: 设计 数据流 单调栈

难度: Medium

设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度

当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

  • 例如,如果未来 7 天股票的价格是 [100,80,60,70,60,75,85],那么股票跨度将是 [1,1,1,2,1,4,6]

实现 StockSpanner 类:

  • StockSpanner() 初始化类对象。
  • int next(int price) 给出今天的股价 price ,返回该股票当日价格的 跨度

示例:

输入:
["StockSpanner", "next", "next", "next", "next", "next", "next", "next"]
[[], [100], [80], [60], [70], [60], [75], [85]]
输出:
[null, 1, 1, 1, 2, 1, 4, 6]

解释:
StockSpanner stockSpanner = new StockSpanner();
stockSpanner.next(100); // 返回 1
stockSpanner.next(80);  // 返回 1
stockSpanner.next(60);  // 返回 1
stockSpanner.next(70);  // 返回 2
stockSpanner.next(60);  // 返回 1
stockSpanner.next(75);  // 返回 4 ,因为截至今天的最后 4 个股价 (包括今天的股价 75) 都小于或等于今天的股价。
stockSpanner.next(85);  // 返回 6
 

提示:

  • 1 <= price <= 105
  • 最多调用 next 方法 104

Submission

运行时间: 220 ms

内存: 21.0 MB

class StockSpanner:

    def __init__(self):
        self.stack = []

    def next(self, price: int) -> int:
        #100 1
        #80 1
        # 60 1
        # 70 2
        # 60 1
        # 75 4
        #85 
        cnt = 1
        while self.stack and self.stack[-1][0] <= price:
            cnt += self.stack.pop()[1]
        self.stack.append((price, cnt))
        return cnt



# https://leetcode.cn/problems/online-stock-span/solutions/1909858/by-lcbin-cfcm/
        




# Your StockSpanner object will be instantiated and called as such:
# obj = StockSpanner()
# param_1 = obj.next(price)

Explain

本题解采用栈(后进先出)的数据结构来解决问题。栈中存储每个价格及其跨度的元组。当调用next函数时,函数会检查栈顶的价格是否小于或等于当前价格;如果是,则栈顶元素被弹出,其跨度被加到当前的跨度计数上。这个过程会一直持续,直到栈为空或栈顶价格大于当前价格。此方法有效地'跳过'了那些小于当前价格的所有连续日期,从而快速计算跨度。

时间复杂度: O(1) 平均时间复杂度

空间复杂度: O(n)

class StockSpanner:

    def __init__(self):
        self.stack = []  # 初始化栈,用于存储价格和对应的跨度

    def next(self, price: int) -> int:
        cnt = 1  # 当日价格的跨度起始为1(至少包括自己)
        # 检查栈顶元素,如果栈顶价格小于等于当前价格,则弹出栈顶
        while self.stack and self.stack[-1][0] <= price:
            cnt += self.stack.pop()[1]  # 将弹出元素的跨度加到当前跨度计数上
        self.stack.append((price, cnt))  # 将当前价格与其计算的跨度压入栈中
        return cnt  # 返回当前价格的跨度

Explore

栈结构被选用是因为它支持快速的后进先出(LIFO)操作,非常适合解决股票价格跨度问题中的逆向查找需求。在计算某一天的价格跨度时,我们需要考虑直到遇到一个更高价格的连续日子。使用栈可以使得在遇到更高的价格时,快速地弹出所有小于等于当前价格的元素,这是队列或普通列表无法高效完成的。队列的先进先出特性和列表的线性访问特点,在这种类型的逆序问题中表现不如栈。

这个条件基于股票价格跨度的定义,即计算从当前日向前连续多少天的价格不超过当前日的价格。如果栈顶的价格小于或等于当前价格,这意味着从栈顶日期到当前日期间的所有价格都无法打断当前价格的跨度计算。因此,可以将这些小于或等于当前价格的日期的跨度累加到当前日期的跨度中,并继续向前寻找,直到找到一个更高的价格。这种方式确保了跨度的正确累计,同时保持算法的高效性。

通过使用栈来'跳过'小于当前价格的连续日期,算法的平均时间复杂度可以达到O(1)。这是因为每个元素最多被压入和弹出栈一次,即使在最坏的情况下,每个元素的操作次数是常数级别的。相比之下,如果使用直接遍历的方法,我们需要从当前日期向前逐一检查,直到找到一个更高的价格或遍历到起始位置。这种方法的时间复杂度为O(n),在数据量大的情况下效率明显低于使用栈的方法。