您好,登录后才能下订单哦!
# C语言的dp算法LeetCode炒股习题案例分析
## 一、动态规划与算法交易概述
动态规划(Dynamic Programming, DP)作为算法设计中的核心思想,在解决最优化问题中展现出独特优势。其通过将复杂问题分解为重叠子问题,并存储中间结果避免重复计算,显著提升算法效率。
在量化交易领域,DP算法尤其适用于解决具有时间序列特性的金融决策问题。股票交易作为典型的序列决策问题,其"买入-卖出"决策过程天然符合DP的问题特征:
1. **多阶段决策**:每个交易日代表一个决策阶段
2. **状态转移**:持仓状态随操作变化
3. **最优子结构**:最终收益取决于子问题的最优解
LeetCode平台收录的股票交易系列题目(如121、122、123、188、309题等)构成了绝佳的DP算法训练场。本文将以C语言实现为核心,深入解析这些经典问题的DP解法。
## 二、基础问题:单次交易(LeetCode 121)
### 问题描述
给定数组`prices`,其中`prices[i]`表示第i天股票价格。限制最多完成一笔交易(买入并卖出),求最大利润。
### 暴力解法分析
```c
int maxProfit(int* prices, int pricesSize) {
int max = 0;
for(int i = 0; i < pricesSize; i++) {
for(int j = i + 1; j < pricesSize; j++) {
int profit = prices[j] - prices[i];
if(profit > max) max = profit;
}
}
return max;
}
时间复杂度O(n²),空间复杂度O(1),效率低下。
定义状态dp[i]
表示前i天的最低价格:
dp[i] = min(dp[i-1], prices[i])
int maxProfit(int* prices, int pricesSize) {
int minPrice = prices[0];
int maxProfit = 0;
for(int i = 1; i < pricesSize; i++) {
if(prices[i] < minPrice) {
minPrice = prices[i];
} else if(prices[i] - minPrice > maxProfit) {
maxProfit = prices[i] - minPrice;
}
}
return maxProfit;
}
时间复杂度O(n),空间复杂度O(1)。
允许进行多次交易,但必须完成前一次交易后才能开始下一次。
int maxProfit(int* prices, int pricesSize) {
int profit = 0;
for(int i = 1; i < pricesSize; i++) {
if(prices[i] > prices[i-1]) {
profit += prices[i] - prices[i-1];
}
}
return profit;
}
定义两种状态:
- dp[i][0]
:第i天不持有股票的最大利润
- dp[i][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-1][0] - prices[i])
int maxProfit(int* prices, int pricesSize) {
int dp0 = 0, dp1 = -prices[0];
for(int i = 1; i < pricesSize; i++) {
int new_dp0 = fmax(dp0, dp1 + prices[i]);
int new_dp1 = fmax(dp1, dp0 - prices[i]);
dp0 = new_dp0;
dp1 = new_dp1;
}
return dp0;
}
卖出股票后需要等待一天才能再次买入。
引入冷却状态:
- dp[i][0]
:持有股票
- dp[i][1]
:不持有且处于冷却
- dp[i][2]
:不持有且非冷却
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])
int maxProfit(int* prices, int pricesSize) {
if(pricesSize == 0) return 0;
int hold = -prices[0];
int coolDown = 0;
int noHold = 0;
for(int i = 1; i < pricesSize; i++) {
int newHold = fmax(hold, noHold - prices[i]);
int newCoolDown = hold + prices[i];
int newNoHold = fmax(noHold, coolDown);
hold = newHold;
coolDown = newCoolDown;
noHold = newNoHold;
}
return fmax(coolDown, noHold);
}
最多完成k次交易时的最大利润。
定义状态dp[i][k][s]
:
- i:天数
- k:剩余交易次数
- s:持仓状态(0/1)
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
int maxProfit(int k, int* prices, int pricesSize) {
if(pricesSize == 0) return 0;
if(k >= pricesSize / 2) {
// 转换为无限交易问题
int profit = 0;
for(int i = 1; i < pricesSize; i++) {
if(prices[i] > prices[i-1]) {
profit += prices[i] - prices[i-1];
}
}
return profit;
}
int* buy = (int*)malloc((k+1) * sizeof(int));
int* sell = (int*)malloc((k+1) * sizeof(int));
for(int i = 0; i <= k; i++) {
buy[i] = -prices[0];
sell[i] = 0;
}
for(int i = 1; i < pricesSize; i++) {
for(int j = 1; j <= k; j++) {
buy[j] = fmax(buy[j], sell[j-1] - prices[i]);
sell[j] = fmax(sell[j], buy[j] + prices[i]);
}
}
int result = sell[k];
free(buy);
free(sell);
return result;
}
问题类型 | 时间复杂度 | 空间复杂度 | 关键突破点 |
---|---|---|---|
单次交易 | O(n) | O(1) | 维护历史最小值 |
无限交易 | O(n) | O(1) | 贪心思想/状态机 |
含冷却期 | O(n) | O(1) | 三状态转换 |
交易次数限制 | O(n*k) | O(k) | 交易次数的状态维度压缩 |
边界条件处理:
if(pricesSize == 0 || k == 0) return 0;
整数溢出防护:
if(profit > INT_MAX - prices[i]) {
// 异常处理
}
DP数组初始化:
dp[0][0] = 0; // 第一天不买
dp[0][1] = -prices[0]; // 第一天买入
内存泄漏检查:
free(dp); // 多维数组需逐层释放
空间优化范式:
// 原始版本
int dp[pricesSize][2];
// 优化版本
int dp0, dp1;
泛化状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
实际交易系统适配:
通过本系列LeetCode股票问题的DP解法分析,我们可以提炼出以下核心经验:
股票交易问题作为DP算法的经典应用场景,其解题思路可延伸至其他序列决策问题,如资源调度、路径规划等。建议读者在理解本文案例的基础上,尝试解决LeetCode上的其他变种问题(如714题含手续费情况),以深化对DP算法的掌握。
“算法交易的本质是将市场机会转化为状态转移方程” —— 量化交易箴言 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。