LC121
最低价雷达 · Best Time to Buy and Sell Stock
一次遍历 · 贪心不是去预测未来,而是每天记住之前见过的最低买入价,再试算「今天卖出能赚多少」,留下历史最大利润就是答案。
输入 · prices
715364
Day 1 买入 Day 4 卖出
最大利润
+5
一次买 + 一次卖 · 买入必须早于卖出
时间 O(n) · 一次遍历空间 O(1) · 两个变量关键不变量:minPrice 单调不增 / bestProfit 单调不降
初始化Step 1 / 6
价格雷达 · 扫描每一天
扫描中:Day 0 · price 7BUY ANCHOR
7
Day 0
1
Day 1
5
Day 2
3
Day 3
6
Day 4
4
Day 5
Day 0 开盘:先把 minPrice 设成 7,profit 设成 0。还没有交易过,没有利润可谈。
金色 · 今日扫描蓝色 · BUY ANCHOR · 历史最低买价绿色 · BEST SELL · 形成最大利润的卖点灰色 · 已扫描过的天
Go 解法 · 与状态同步
high-lighted: 2, 31func maxProfit(prices []int) int {2 minPrice := prices[0]3 profit := 04 for i := 1; i < len(prices); i++ {5 if prices[i] < minPrice {6 minPrice = prices[i]7 }8 if prices[i]-minPrice > profit {9 profit = prices[i] - minPrice10 }11 }12 return profit13}
蓝色行 = 更新 minPrice · 绿色行 = 刷新 profit · 金色 = 当前正在扫描。
状态面板 · 这一帧的全部状态
当前天
Day 0
当前价格
7
minPrice
7
candidate = price − minPrice
+0
bestProfit
+0
本步动作
初始化
不变量
- minPrice = 扫描至今见过的最低买入价只要今天的价格比 minPrice 更低,就刷新它。否则保持不变。它只会变小,不会变大。
- bestProfit = 扫描至今能拿到的最大利润每天都问一句『如果今天卖出,能赚多少?』把答案和 bestProfit 比一下,谁大谁留下。它只会变大,不会变小。
- 每一天只需回答两个小问题① 今天有没有刷新最低买价? ② 今天卖出,是否比之前更赚?两个判断之外什么都不做。
执行轨迹 · 每一帧的 before / after
点击行跳转| Frame | Day | price | minPrice (before → after) | candidate | bestProfit (before → after) | 动作 |
|---|---|---|---|---|---|---|
| 0 | 0 | 7 | 7 → 7 | +0 | 0 → 0 | 初始化 |
| 1 | 1 | 1 | 7 → 1 | +0 | 0 → 0 | 更新历史最低买价 |
| 2 | 2 | 5 | 1 → 1 | +4 | 0 → 4 | 刷新最大利润 |
| 3 | 3 | 3 | 1 → 1 | +2 | 4 → 4 | 保持现有最大利润 |
| 4 | 4 | 6 | 1 → 1 | +5 | 4 → 5 | 刷新最大利润 |
| 5 | 5 | 4 | 1 → 1 | +3 | 5 → 5 | 保持现有最大利润 |
面试 30 秒答题模板
这道题只允许一次买卖,且买入必须早于卖出,所以我做 一次遍历: 从第 0 天开始,维护一个 历史最低买价 minPrice;从第 1 天开始,每一天先看是否要把 minPrice 刷新得更低,再 试算 prices[i] − minPrice 作为「今天就卖出能拿到的利润」,并和已有的最大利润比较,谁大留谁。 遍历完成后返回 bestProfit。 时间 O(n)、空间 O(1)。 正确性来自两条不变量:minPrice 永远是扫描至今的最低买价; bestProfit 永远是扫描至今的最大可得利润,因此整段扫描结束时 bestProfit 就是全局最优。
常见误区 · 别再翻车
- 把 LC121 当 LC122LC121 只允许一次买卖;LC122 才允许多次交易(捕捉所有上升段)。一上来累加涨幅就跑偏了。
- 用全局 max(prices) − min(prices)不行。买入必须早于卖出,全局最大值可能出现在最低价之前,那笔交易是非法的。
- 以为算法在预测未来并没有。每一天的 minPrice 只来自已经扫描过的天,永远不偷看后面的价格。
- 把 minPrice 当成全局最低价minPrice 是动态维护的状态:在 Day i 这一刻,它只代表 prices[0..i] 的最低价,而不是整段数组的最低价。