从K线图到代码:当算法成为"股神"的操盘秘籍

98 阅读10分钟

引言

——深度解析股票交易三题的核心思想与思维跃迁

当你盯着屏幕上红绿交错、上蹿下跳的K线图时,是否想过:那些让散户抓耳挠腮的买卖时机问题,其实早已被算法精准解构?LeetCode 上的三道经典股票交易题(121、122、123)不仅是面试高频题,更是一场关于选择、策略与思维方式的思维训练。

这三道题层层递进,从"只能买卖一次"到"无限次交易",再到"最多两次交易",背后藏着两种核心算法范式:贪心算法动态规划。它们不只是代码技巧,更是应对复杂决策的智慧模型。

本文将不再满足于"给出代码 + 简单解释",而是深入剖析每道题的本质结构、关键洞察点和思维跃迁过程,帮助你真正理解:"为什么这个思路成立?"、"为什么非得这么设计状态?"、"能不能推广到更多次交易?"


一、LeetCode 121:只能买卖一次 —— "人生没有后悔药"

📌 题目重述:

给定一个数组 pricesprices[i] 表示第 i 天的股价。你最多只能完成一笔交易(即买一次、卖一次),求最大利润。

示例:[7,1,5,3,6,4] → 最大利润为 5(第2天买,第5天卖)

❌ 常见误区:暴力枚举所有组合

最直观的想法是双重循环,枚举所有 i < j 的 (buy, sell) 组合,计算差值取最大。
时间复杂度 O(n²),虽然能过小数据,但在面试中几乎等于"出局"。

✅ 正确打开方式:逆向思维 + 贪心维护历史最优

让我们换一个角度思考:

"如果我在第 i 天卖出,那么为了利润最大,我应该在哪一天买入?"

答案很明确:在第 i 天之前价格最低的那一天买入。

也就是说,对于每一个"卖出日" ,我们都希望找到它前面的"最低买入价"。

于是我们可以边遍历数组,边维护两个变量:

image.png

遍历每一天的价格 price

  1. 更新 minPrice = Math.min(minPrice, price)
  2. 计算"今天卖出"的利润:profit = price - minPrice
  3. 更新 maxProfit = Math.max(maxProfit, profit)

这样,我们在一次遍历中就完成了全局最优解的搜索。

💡 关键洞察点:

这个问题的本质是:在一个序列中找一对索引 (i, j),满足 i < j,使得 prices[j] - prices[i] 最大。

这等价于求"最大后缀差"。而贪心策略巧妙地将其转化为一个在线更新问题,避免了回溯或预处理。

天数价格minPrice当天利润maxProfit
077-0
11100
25144
33124
46155
54135

可以看到,第4天(价格6)时,我们找到了最大利润5(6-1)。

🧠 深层理解:

  • 我们不需要知道未来的价格,只需要基于已知信息做出当前最优决策。
  • 这正是贪心算法的精髓:局部最优可以推出全局最优。
  • 成立的前提是:只能交易一次 → 决策具有无后效性,且目标函数可分解。

✅ 时间复杂度:O(n)
✅ 空间复杂度:O(1)

🌰 代码实现:

image.png 这段代码的精妙之处在于:它用最简单的两个变量,完成了对整个交易过程的建模。我们不需要知道"什么时候买入",只需要知道"当前最低价是多少";我们也不需要知道"什么时候卖出",只需要知道"如果今天卖出能赚多少"。


二、LeetCode 122:可以无限次买卖 —— "积小胜为大胜"

📌 题目重述:

允许进行任意多次交易(但不能同时持有多股,必须先卖后买),求最大总利润。

示例:[7,1,5,3,6,4] → 可以 1买5卖(+4),3买6卖(+3),总利润 7

❌ 直觉陷阱:找所有上升波段?

有人会想:"那我就找出所有的上升区间,比如 [1→5]、[3→6],然后分别操作。"
听起来合理,但实现起来需要判断何时开始/结束一段上涨,容易出错。

✅ 更优雅的贪心视角:把每一次正向波动都当作机会

关键洞察来了:

任何一次价格上涨,都可以被捕捉为利润!

比如:

  • 第2天到第3天:1 → 5,涨了4 → 利润+4
  • 第3天到第4天:5 → 3,跌了 → 不操作
  • 第4天到第5天:3 → 6,涨了3 → 利润+3

你会发现,只要把所有 prices[i] > prices[i-1] 的差值加起来,结果就是最大利润!

天数价格与前一天差值是否累加累计利润
07--0
11-60
25+44
33-24
46+37
54-27

可以看到,我们累加了两次上涨(4和3),总利润为7。

💡 为什么这是对的?

数学上可以证明:

多次交易的最大利润 = 所有上升区间的总和

为什么?因为你可以"虚拟持有":

  • 如果明天会涨,今天就买入;
  • 如果明天会跌,今天就卖出;

虽然现实中无法预知未来,但在算法中,这种"差分累加"等价于你在每个上涨日都完成了"低买高卖"。

🎯 举个例子:[1,3,5]

  • 差分法:(3-1)+(5-3)=2+2=4
  • 实际操作:1买5卖,利润4 → 完全一致!

这说明:即使不允许"当天买卖",这种策略也能通过连续操作实现相同效果

🧠 思维跃迁:

  • 不再关注"什么时候买入/卖出",而是关注"趋势方向"。
  • 将复杂的区间划分问题,简化为简单的差分判断。
  • 贪心依然成立,因为每次上涨都是独立可捕获的机会。

✅ 时间复杂度:O(n)
✅ 空间复杂度:O(1)

🌰 代码实现:

image.png 这段代码的精妙之处在于:它完全忽略了交易的具体时机,只关注价格变化的方向。就像一个永远乐观的投资者,只要市场有上涨,就抓住每一滴利润。


三、LeetCode 123:最多两次交易 —— 动态规划的经典教学案例

📌 题目重述:

最多允许进行 两笔交易,求最大利润。

示例:[3,3,5,0,0,3,1,4] → 最优解是两次交易:0→3 和 1→4,总利润6

❌ 为什么贪心失效?

你可能会想:"先做一次最优交易,再在剩余区间做第二次?"
错!因为第一次交易的选择会影响第二次的操作空间。

比如:

  • 如果你在 [0,3] 卖出太早,可能错过后面的低价 [1]
  • 或者第一次没卖,导致第二次资金不足……

这说明:决策之间存在依赖关系,不能再用贪心"分而治之"。

我们必须考虑全局状态转移

✅ 正确解法:动态规划建模四个状态

我们将整个交易过程拆解为四个阶段:

状态含义初始值
buy1第一次买入后的最大利润(花钱,所以是负数)-prices[0]
sell1第一次卖出后的最大利润0
buy2第二次买入后的最大利润-prices[0]
sell2第二次卖出后的最大利润(最终答案)0

注意:这里的"利润"是相对初始资金而言的。例如 buy1 = -3 表示花了3块钱买了股票,当前净收益是-3。

🔁 状态转移逻辑(逐日更新):

对每一天的价格 price[i],我们按顺序更新四个状态:

image.png

💡 为什么这个顺序是对的?

关键在于:我们假设每天都可以"反悔"之前的决策

比如:

  • 昨天 buy1 = -5,今天股价跌到3 → 我们可以把 buy1 更新为 -3,相当于"撤回昨天的买入,改成今天更低点买入"。
  • 同理,sell1 可以后续更新为更高点卖出。

这种"允许反悔"的机制,正是 DP 强大的地方:它不是模拟真实交易流程,而是维护"截至目前所有可能路径中的最优状态"。

🧠 深层理解:状态机视角

我们可以把这个过程看作一个有限状态机:

start → buy1 → sell1 → buy2 → sell2

每一天,我们都尝试从上一个状态"推进"到下一个状态,或者保持原地。DP 的作用就是在这条链路上选出最优路径。

🌰 以实例 [3,3,5,0,0,3,1,4] 为例:

让我们一步步追踪状态变化:

天数价格buy1sell1buy2sell2
03-30-30
13-30-30
25-32-30
300222
400222
530225
610215
740216

可以看到,最终 sell2 = 6,即最大利润为6,与题目描述一致。

✅ 推广性极强!

这套模式可以轻松扩展到 k 次交易:

image.png

image.png 这段代码的精妙之处在于:它用四个变量构建了一个"状态机",每个变量都代表了交易过程中的一个关键节点。通过每天更新这些状态,我们实际上是在模拟所有可能的交易路径,并始终保留最优解。


🧩 总结对比:三种策略的本质区别

问题交易次数核心思想算法范式是否依赖历史状态
1211次抓住最低买入 + 最高卖出贪心只需维护最低价
122无限次所有上涨都要赚贪心每步独立
123最多2次平衡前后两次交易动态规划必须记录中间状态

🎯 核心结论:

  • 贪心适用于"局部最优可累积成全局最优"的场景,如单次交易、每次上涨都赚钱。
  • 动态规划适用于"多阶段决策且状态依赖"的问题,尤其是有限次数的资源分配类问题。

🌟 写在最后:算法即人生

这三道题不只是编程练习,更像是人生的隐喻:

  • 新手期:追求"一击必杀",幻想抄到底部逃到顶部 → 结果往往踏空或套牢。
  • 成长期:学会"见好就收",抓住每一个微小机会 → 积累复利。
  • 成熟期:懂得"布局未来",权衡短期利益与长期规划 → 实现系统性胜利。

而算法的魅力就在于:它用最严谨的逻辑,告诉我们——
在不确定的世界里,如何做出确定的选择

🧠 个人思考:

在刷题的过程中,我逐渐意识到:算法题的本质不是"解题",而是"建模"。我们需要把现实问题抽象成一个数学模型,然后找到最适合的算法范式。

股票交易题教会我的最重要一课是:不要被表象迷惑。当你看到"股票"、"交易"这些词时,很容易陷入金融思维,想着如何预测价格走势。但算法思维告诉我们:不需要预测未来,只需要对已知信息做出最优响应

这就像人生中的很多决策:我们无法预知未来,但可以基于当前信息做出最佳选择。贪心算法教会我们抓住当下,动态规划教会我们规划未来。