问题简介
题解github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions
题目描述
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格。
你可以在以下限制条件下完成尽可能多的交易(多次买卖一支股票):
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票(即存在 1 天的冷冻期)。
目标:计算你能获得的最大利润。
📌 示例说明
示例 1:
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例 2:
输入: prices = [1]
输出: 0
💡 解题思路
本题是经典的「股票买卖」系列问题之一,加入了「冷冻期」这一新约束。核心在于 状态机建模 或 动态规划。
方法一:状态机动态规划(推荐)
步骤 1:定义状态
每天结束时,我们可能处于以下三种状态之一:
hold:持有股票(今天买入或之前买入未卖出)sold:今天刚卖出股票(明天进入冷冻期)rest:不持有股票,且不在冷冻期(可以买入)
注意:
rest包括两种情况:从未买过,或冷冻期已结束。
步骤 2:状态转移方程
-
hold[i] = max(hold[i-1], rest[i-1] - prices[i])
→ 要么继续持有,要么从rest状态买入 -
sold[i] = hold[i-1] + prices[i]
→ 只能从持有状态卖出 -
rest[i] = max(rest[i-1], sold[i-1])
→ 要么继续保持空仓,要么从昨天的sold(冷冻期结束)转来
步骤 3:初始化
- 第 0 天:
hold = -prices[0](买入)sold = 0(无法卖出)rest = 0(不操作)
步骤 4:最终答案
最后一天,最大利润为 max(sold, rest)(不可能持有股票,因为持有不如卖出)
方法二:空间优化(滚动变量)
由于每天只依赖前一天的状态,可用三个变量代替数组,将空间复杂度降至 O(1)。
方法三:另一种状态定义(可选)
也可以定义 dp[i][0/1] 表示第 i 天是否持有股票,但需额外处理冷冻期逻辑(如 dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]),而 dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])),因为买入只能在非冷冻期,即至少两天前卖出。
这种方法也可行,但状态机更清晰。
💻 代码实现
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int hold = -prices[0]; // 持有股票
int sold = 0; // 今天卖出
int rest = 0; // 空仓且非冷冻
for (int i = 1; i < prices.length; i++) {
int prevHold = hold;
int prevSold = sold;
int prevRest = rest;
hold = Math.max(prevHold, prevRest - prices[i]);
sold = prevHold + prices[i];
rest = Math.max(prevRest, prevSold);
}
return Math.max(sold, rest);
}
}
func maxProfit(prices []int) int {
if len(prices) == 0 {
return 0
}
hold := -prices[0] // 持有股票
sold := 0 // 今天卖出
rest := 0 // 空仓且非冷冻
for i := 1; i < len(prices); i++ {
prevHold, prevSold, prevRest := hold, sold, rest
hold = max(prevHold, prevRest - prices[i])
sold = prevHold + prices[i]
rest = max(prevRest, prevSold)
}
return max(sold, rest)
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
🧪 示例演示
以 prices = [1,2,3,0,2] 为例:
| Day | Price | hold | sold | rest |
|---|---|---|---|---|
| 0 | 1 | -1 | 0 | 0 |
| 1 | 2 | max(-1, 0-2)=-1 | -1+2=1 | max(0,0)=0 |
| 2 | 3 | max(-1,0-3)=-1 | -1+3=2 | max(0,1)=1 |
| 3 | 0 | max(-1,1-0)=1 | -1+0=-1 | max(1,2)=2 |
| 4 | 2 | max(1,2-2)=1 | 1+2=3 | max(2,-1)=2 |
最终 max(sold=3, rest=2) = 3 ✅
✅ 答案有效性证明
- 状态覆盖完整:三种状态互斥且穷尽所有可能。
- 转移正确:
- 买入只能从
rest(非冷冻)出发; - 卖出后必进入
sold,次日自动变为rest的候选; rest可由自身延续或从sold转入(冷冻期结束)。
- 买入只能从
- 边界处理正确:第 0 天初始化合理。
- 最优子结构:每一天的最优解由前一天的最优解推导而来。
因此,算法正确。
📊 复杂度分析
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
仅使用常数个变量,遍历一次数组。
📌 问题总结
- 关键点:引入「冷冻期」后,不能简单用
buy/sell两状态,需增加rest状态区分是否可买入。 - 技巧:状态机建模是处理带约束 DP 问题的强大工具。
- 扩展:类似问题如「含手续费」「最多 k 次交易」等,均可通过扩展状态解决。
- 记忆口诀:
“持、卖、歇,三态流转;
买从歇起,卖入持中;
冷冻一日,歇承卖终。”
309. 买卖股票的最佳时机含冷冻期&spm=1001.2101.3001.5002&articleId=158285276&d=1&t=3&u=8db492bcc1044fbd80776bab28057495)
1235

被折叠的 条评论
为什么被折叠?



