动态规划优化实战:买卖股票的最佳时机IV动画教程
你是否曾在面对"买卖股票的最佳时机"系列问题时感到困惑?尤其是当交易次数限制为k次时,如何设计高效算法?本文将通过动画模拟方式,带你从零掌握动态规划优化技巧,轻松解决LeetCode困难级问题。
问题定义与痛点分析
给定一个整数数组prices,其中第i个元素代表某支股票第i天的价格。设计一个算法计算你所能获取的最大利润,要求最多完成k笔交易。注意:你不能同时参与多笔交易(必须在再次购买前出售掉之前的股票)。
传统解法存在两大痛点:
- 暴力枚举时间复杂度高达O(n²k),无法应对大数据
- 状态定义不清晰导致边界条件处理复杂
- 空间复杂度优化困难
动态规划基础框架
状态定义
我们定义dp[i][j][0/1]表示第i天,最多进行j笔交易,当前是否持有股票(0表示不持有,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])
初始条件
dp[0][j][0] = 0 // 第一天不持有股票,利润为0
dp[0][j][1] = -prices[0] // 第一天买入股票,利润为负价格
空间优化核心技巧
滚动数组降维
观察状态转移方程发现,第i天状态只与i-1天相关,可将三维数组优化为二维:
// 原始三维数组
int[][][] dp = new int[n][k+1][2];
// 优化为二维数组
int[][] dp = new int[k+1][2];
交易次数优化处理
当k >= n/2时,问题退化为"无限次交易",可直接使用贪心算法:
if (k >= prices.length/2) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i-1]) {
maxProfit += prices[i] - prices[i-1];
}
}
return maxProfit;
}
完整实现代码
public int maxProfit(int k, int[] prices) {
if (prices == null || prices.length == 0) return 0;
int n = prices.length;
// 处理k过大情况
if (k >= n/2) {
int maxProfit = 0;
for (int i = 1; i < n; i++) {
if (prices[i] > prices[i-1]) {
maxProfit += prices[i] - prices[i-1];
}
}
return maxProfit;
}
// 初始化二维数组
int[][] dp = new int[k+1][2];
for (int j = 0; j <= k; j++) {
dp[j][0] = 0;
dp[j][1] = -prices[0];
}
// 状态转移
for (int i = 1; i < n; i++) {
for (int j = k; j >= 1; j--) {
dp[j][0] = Math.max(dp[j][0], dp[j][1] + prices[i]);
dp[j][1] = Math.max(dp[j][1], dp[j-1][0] - prices[i]);
}
}
return dp[k][0];
}
动画模拟与状态变化
下图展示了k=2时的状态变化过程:
时间与空间复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 原始三维DP | O(nk) | O(nk) |
| 滚动数组优化 | O(nk) | O(k) |
| 贪心算法(k≥n/2) | O(n) | O(1) |
实际应用与扩展
该算法可直接应用于LeetCode 188题"买卖股票的最佳时机IV",同时其优化思想也适用于:
- 含冷冻期的股票交易问题
- 含手续费的股票交易问题
- 多维度约束的动态规划场景
总结与学习资源
通过本文学习,你已掌握:
- 动态规划状态定义的核心技巧
- 滚动数组实现空间优化
- 特殊情况的贪心算法处理
更多动画教程请参考项目中的动态规划模块,或访问作者面试网站获取交互式学习体验。建议结合前缀和技术进一步巩固动态规划思想。
提示:解决动态规划问题的关键在于找到状态转移方程,建议通过画状态图辅助思考。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



