这道题目来自力扣(LeetCode)的股票买卖系列问题,编号122。题目描述为:给定一个数组 prices,其中 prices[i] 表示某支股票第 i 天的价格。你可以尽可能地完成更多的交易(多次买卖一支股票),但必须在再次购买前出售掉之前的股票。要求计算你能获得的最大利润。
这个问题在实际投资中非常典型——它模拟了短线交易者在波动市场中的操作策略。与只能买卖一次的基础版本(121题)不同,本题允许无限次交易,这更接近现实中的日内交易场景。
最直观的解法是尝试所有可能的买卖组合。对于n天的价格序列,存在2^(n-1)种可能的交易路径(每天可以选择持有或卖出)。这种O(2^n)时间复杂度的解法在n>20时就不可行了,而力扣的测试用例往往n>10^5。
注意:虽然回溯法理论上可行,但在算法题中遇到"尽可能多交易"的描述时,通常暗示存在更优解。
通过观察价格波动规律可以发现:总最大利润等于所有上升区间的累加。例如价格序列[1,3,5]的利润4=(3-1)+(5-3),等同于(5-1)。这满足贪心选择性质——局部最优解能构成全局最优解。
数学证明:
虽然贪心算法更高效,但动态规划解法更具通用性,可以扩展到更复杂的交易限制场景(如含手续费、冷冻期等)。定义状态:
状态转移方程:
python复制dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) # 保持空仓或卖出
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) # 保持持仓或买入
空间复杂度可优化到O(1),因为每天的状态只依赖前一天。
python复制def maxProfit(prices):
profit = 0
for i in range(1, len(prices)):
if prices[i] > prices[i-1]:
profit += prices[i] - prices[i-1]
return profit
时间复杂度:O(n) 单次遍历
空间复杂度:O(1) 常数空间
python复制def maxProfit(prices):
n = len(prices)
dp = [[0]*2 for _ in range(n)]
dp[0][1] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
return dp[-1][0]
空间优化版本:
python复制def maxProfit(prices):
cash, hold = 0, -prices[0]
for price in prices[1:]:
cash, hold = max(cash, hold + price), max(hold, cash - price)
return cash
| 算法类型 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力搜索 | O(2^n) | O(n) | 仅理论分析 |
| 贪心算法 | O(n) | O(1) | 无交易限制 |
| 动态规划 | O(n) | O(n)/O(1) | 可扩展复杂场景 |
贪心算法在本题中是最优解,但当问题变化时:
python复制# 错误示例:只在全局最低点买入,最高点卖出
min_price, max_profit = float('inf'), 0
for price in prices:
min_price = min(min_price, price)
max_profit = max(max_profit, price - min_price) # 只计算单次交易
python复制# 错误示例:每天买卖(忽略手续费)
profit = 0
for i in range(1, len(prices)):
profit += prices[i] - prices[i-1] # 包含下跌日的负收益
有效测试组合:
python复制for i in range(1, len(prices)):
if prices[i] > prices[i-1]:
print(f"Day {i}: Buy at {prices[i-1]}, Sell at {prices[i]}")
profit += prices[i] - prices[i-1]
python复制import matplotlib.pyplot as plt
plt.plot(prices)
plt.scatter(range(len(prices)), prices, c='r')
plt.show()
在实际高频交易中,类似的策略被称为"趋势跟踪":
python复制def maxProfit(prices, fee):
cash, hold = 0, -prices[0]
for price in prices[1:]:
cash = max(cash, hold + price - fee)
hold = max(hold, cash - price)
return cash
python复制def maxProfit(prices):
n = len(prices)
if n < 2: return 0
dp = [[0]*3 for _ in range(n)] # 0:hold, 1:cooldown, 2:empty
dp[0][0] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
dp[i][1] = dp[i-1][0] + prices[i]
dp[i][2] = max(dp[i-1][2], dp[i-1][1])
return max(dp[-1][1], dp[-1][2])
对于面试场景:
在实际开发中,建议使用动态规划框架,虽然本题中贪心更高效,但DP的代码结构更容易适应需求变更。比如当产品经理突然要求加入0.1%的手续费时,贪心算法可能需要完全重写,而DP只需修改一个参数。