题目重点:用历史最低价一次扫描计算单次买卖最大利润,澄清买卖顺序、空输入和无盈利时的返回值。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版价格为非负整数,必须先买后卖;可选择不交易,故最大收益至少为 0。
- 不考虑手续费、冷冻期或多次交易,这些会改变问题。
解题路线
- 从左到右维护迄今最低买价,并用当前价减该最低价作为“今天卖出”的最佳候选。
- 当前价更低则更新最低价;否则更新最佳利润。这个顺序保证候选买入时间不晚于卖出时间,且同一天买卖不会产生正收益。
- 不变量:扫描到第 j 天后,最低价是前 j 天最小值,best 是所有卖出日不晚于 j 的合法交易最大收益;下一天只需考虑以今天为卖出日的新交易。
手动走一遍
[7,1,5,3,6,4] 的最低价从 7 降为 1,随后卖出价 6 给利润 5。若 [7,6,4,3,1] 单调下降,返回 0 而非负数。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
"""Maximum profit with one buy before one sell; zero if no profitable trade."""
def max_single_trade_profit(prices: list[int]) -> int:
lowest: int | None = None
best = 0
for price in prices:
if lowest is None or price < lowest:
lowest = price
else:
best = max(best, price - lowest)
return best
复杂度
N 个价格一次扫描 O(N) 时间、O(1) 额外空间;若要输出买卖日期,额外维护最低价下标与最佳交易下标,空间仍 O(1)。
易错点与追问
容易失分的边界
- 取全局最小值和全局最大值相减,可能让买入发生在卖出之后。
- 在全下降输入返回负利润,忽略不交易选择。
- 误解为不限交易次数的股票题。
继续深挖
- 如果必须交易一次,下降序列的答案应如何定义?
- 加入手续费或最多两次交易后,状态与复杂度如何变化?
