伴学笔记:股票价格波动问题——栈的应用

128 阅读5分钟

在本次学习中,我们探讨了如何通过栈来解决一个典型的股票交易问题:给定股票的价格列表,计算从每一天开始,至少需要等待多少天才能看到价格上涨。这个问题本质上是一个“下一个更大元素”的问题,适合通过栈来优化解决方案。接下来,我将从问题描述、思路解析、栈的原理以及实现细节等方面进行详细讲解。

一、问题背景与目标

给定一个股票价格的列表,要求我们为每个位置计算从那一天开始,至少需要等待多少天才能看到价格上涨。如果从那一天开始没有上涨的情况,则输出0。具体来说,假设我们有以下的价格序列:

[33, 34, 14, 12, 16]

我们希望输出一个新的列表,表示每一天从该天开始需要等待多少天才能看到价格上涨。对于上述价格列表,期望的输出为:

[1, 0, 2, 1, 0]

这意味着:

  • 从第 1 天开始,价格会在第 2 天上涨(等待 1 天)。
  • 从第 2 天开始,价格不会再上涨,输出 0。
  • 从第 3 天开始,价格会在第 5 天上涨(等待 2 天)。
  • 从第 4 天开始,价格会在第 5 天上涨(等待 1 天)。
  • 从第 5 天开始,价格不会再上涨,输出 0。

二、栈的应用与思路分析

要解决这个问题,我们可以借助栈的特性来高效地找出每个元素之后的下一个较大元素。栈是一种先进后出的数据结构,它允许我们在遍历过程中,快速找出当前元素在后续序列中的“下一个更大元素”位置。

核心思想:

  • 从后往前遍历价格列表,使用栈存储价格的索引。
  • 每当我们处理一个新的价格时,栈中保存的就是尚未被处理的、价格较大的天数索引。
  • 如果当前价格比栈顶的价格小,则继续向前遍历,直到找到一个较大的价格,或者栈为空为止。
  • 如果栈中有元素,则说明栈顶索引对应的天数比当前天的价格大,表示从当前天起到栈顶天数为止,有价格上涨。

具体操作:

  1. 栈的初始化: 栈用于保存价格的索引。我们从最后一天开始遍历价格列表。
  2. 判断条件: 如果当前价格小于栈顶价格对应的元素,说明当前天没有上涨,栈顶的索引将被弹出,直到找到一个比当前价格大的价格或栈为空。
  3. 计算等待天数: 如果栈中仍然有元素,说明我们找到了一个价格上涨的日期,计算从当前日期到那个日期的天数差。
  4. 栈的更新: 每处理完一天,当前天的索引会被压入栈中,供后续天数参考。

三、栈的详细操作过程

通过栈来解决这个问题,不仅避免了暴力枚举的 O(N^2) 时间复杂度,还能高效地在 O(N) 时间内完成计算。具体来说,我们从最后一天开始,逐步将天数索引压入栈中:

  1. 初始化: 创建一个空栈,用来存储价格的索引。对于每一天,判断当前价格是否小于栈顶的价格。

  2. 遍历过程: 从后往前遍历价格列表,处理每个天数:

    • 如果当前价格大于栈顶的价格,表示找到了一个上涨的天数,将结果更新为从当前天到栈顶天数的天数差。
    • 如果栈顶的价格大于或等于当前价格,说明当前天没有上涨,弹出栈顶元素,直到找到较大的价格。
  3. 栈的更新: 在每次处理完一个天数后,将该天的索引压入栈中,供下次遍历时使用。

四、时间与空间复杂度

  • 时间复杂度: 每个元素最多被压入栈一次并弹出一次,因此时间复杂度为 O(N),其中 N 为股票价格的数量。相比暴力算法的 O(N^2) 复杂度,栈的应用显著提高了算法效率。
  • 空间复杂度: 栈的大小与输入数组的大小成正比,最坏情况下栈中会存储所有元素。因此,空间复杂度为 O(N)。

五、代码实现

def solution(N: int, stockPrices: list) -> list:
    result = [0] * N  # 初始化结果列表
    stack = []  # 栈用于存储价格索引

    # 遍历股票价格列表
    for i in range(N - 1, -1, -1):  # 从后往前遍历
        # 弹出栈顶索引,直到找到比当前价格大的价格
        while stack and stockPrices[i] >= stockPrices[stack[-1]]:
            stack.pop()
        
        # 如果栈不为空,说明栈顶元素的价格大于当前价格
        if stack:
            result[i] = stack[-1] - i  # 计算从当前天到下一次价格上涨的天数

        # 将当前天的索引压入栈
        stack.append(i)

    return result

六、总结与反思

在本次学习中,我们使用了栈来高效地解决“股票价格波动”问题。通过栈存储未处理的索引,我们避免了多次遍历,达到了 O(N) 的时间复杂度。栈的使用不仅是解决此问题的关键,也是许多“下一个更大元素”类型问题的经典解决方案。

此外,通过本次练习,我深刻认识到栈在处理序列问题中的应用,如股票价格、括号匹配等,都可以极大地简化问题的复杂度。栈是一种非常实用的基础数据结构,它在很多算法中的作用不容忽视。

总之,掌握栈的应用及其优化方法,对于提升算法设计能力是非常有帮助的。这不仅让我更加深入理解了栈的本质,也为以后解决更复杂的算法问题打下了坚实的基础。