抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

股票买卖问题是 LeetCode 中经典的动态规划题型,通常要求在给定的股价数组中,通过买入和卖出股票来获取最大利润。这类问题考验程序员对动态规划和贪心算法的理解和应用。

问题类型分类

股票买卖问题主要包括以下几种典型场景:

单次交易

  • 问题描述:只能进行一次买入和卖出交易
  • 解题关键:找出买入点和卖出点的最大利润差

多次交易(不限制交易次数)

  • 问题描述:可以进行多次买卖,但每次只能持有一股
  • 解题关键:贪心算法,只要存在利润就进行交易

有冷却期的多次交易

  • 问题描述:卖出股票后,需要休息一定天数才能继续交易
  • 解题关键:增加状态转移的复杂性,需要记录冷却状态

有手续费的多次交易

  • 问题描述:每次交易需要扣除手续费
  • 解题关键:在计算利润时需要减去手续费

最多进行K次交易

  • 问题描述:限制最大交易次数
  • 解题关键:需要使用更复杂的动态规划状态转移

解题模板与思路

动态规划通用思路

  1. 定义状态数组
  2. 确定状态转移方程
  3. 初始化边界条件
  4. 计算最终结果

单次交易示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def maxProfit(prices):
if not prices:
return 0

min_price = float('inf')
max_profit = 0

for price in prices:
# 更新最低买入价
min_price = min(min_price, price)
# 计算当前最大利润
max_profit = max(max_profit, price - min_price)

return max_profit

多次交易示例代码

1
2
3
4
5
6
7
8
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

冷却期交易示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def maxProfit(prices):
if len(prices) < 2:
return 0

# dp[i][0]: 不持有股票且不在冷却期的最大利润
# dp[i][1]: 不持有股票且在冷却期的最大利润
# dp[i][2]: 持有股票的最大利润
dp = [[0, 0, 0] for _ in range(len(prices))]

# 初始状态
dp[0][2] = -prices[0]

for i in range(1, len(prices)):
# 不持有股票且不在冷却期:可以是前一天就不持有,或者今天卖出股票
dp[i][0] = max(dp[i-1][0], dp[i-1][1])

# 不持有股票且在冷却期:必须是今天卖出股票
dp[i][1] = dp[i-1][2] + prices[i]

# 持有股票:可以是前一天就持有,或者今天买入股票
dp[i][2] = max(dp[i-1][2], dp[i-1][0] - prices[i])

# 最后返回不持有股票的最大利润
return max(dp[-1][0], dp[-1][1])

K次交易示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def maxProfit(k, prices):
if not prices or k == 0:
return 0

# 如果k很大,相当于可以无限次交易
if k >= len(prices) // 2:
return sum(max(0, prices[i] - prices[i-1]) for i in range(1, len(prices)))

# dp[i][j][0]: 第i天,已进行j次交易,当前不持有股票的最大利润
# dp[i][j][1]: 第i天,已进行j次交易,当前持有股票的最大利润
dp = [[[0, float('-inf')] for _ in range(k + 1)] for _ in range(len(prices))]

# 初始状态
for j in range(k + 1):
dp[0][j][1] = -prices[0]

for i in range(1, len(prices)):
for j in range(1, k + 1):
# 不持有股票:要么前一天就不持有,要么今天卖出
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i])

# 持有股票:要么前一天就持有,要么今天买入
dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])

# 返回最后不持有股票的最大利润
return max(dp[-1][j][0] for j in range(k + 1))

含手续费的交易示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def maxProfit(prices, fee):
if len(prices) < 2:
return 0

# dp[i][0]: 不持有股票的最大利润
# dp[i][1]: 持有股票的最大利润
dp = [[0, 0] for _ in range(len(prices))]

# 初始状态
dp[0][1] = -prices[0]

for i in range(1, len(prices)):
# 不持有股票:要么前一天就不持有,要么今天卖出股票(扣除手续费)
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee)

# 持有股票:要么前一天就持有,要么今天买入股票
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])

# 返回最后不持有股票的最大利润
return dp[-1][0]

解题技巧

  1. 贪心vs动态规划

    • 简单场景用贪心算法
    • 复杂场景使用动态规划
  2. 状态定义

    • 通常定义两个状态:持有股票和不持有股票
    • 记录每个状态下的最大利润
  3. 边界处理

    • 注意数组为空的情况
    • 处理初始状态的特殊情况

常见误区

  • 忽略边界条件
  • 没有正确处理状态转移
  • 未考虑交易限制条件

后记

股票买卖问题是动态规划的经典应用,需要仔细分析问题的具体约束条件,选择合适的算法策略。通过反复练习和总结,可以逐步掌握解题的核心思路。

评论