动态规划专练:力扣第121、122题

力扣第121题-买卖股票的最佳时机

2026.8.6

1.因为只买卖一次,所以很容易想到使用遍历一遍数组的贪心算法,遍历过程中更新遇到的最小值,并不断更新利润最大值。完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // low 记录遍历到当前为止的历史最低股价
 3.     int low = INT_MAX;
 4.     // res 记录最大利润,初始为0(不交易利润为0)
 5.     int res = 0;
 6.     for (int i = 0; i < pricesSize; i++){
 7.         // 更新最低买入价格
 8.         low = fmin(low, prices[i]);
 9.         // 当天卖出,更新最大利润
10.         res = fmax(res, prices[i] - low);
11.     }
12.     return res;
13. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

2026.8.7

1.本题也可以使用动态规划来解决。当天的状态只有两种:持有股票和未持有股票,所以可以设置一个二维数组dppricesSize2,dpi0代表第i天持有股票的最大现金,dpi1代表第i未持有股票的最大现金。初始现金为0,买入股票后现金变为-pricesi

2.分类讨论来推导出递推公式:

(1)持有股票。又分为两种情况:之前买入和今天买入,对应的金额分别为dpi - 10和-pricesi,则递推公式为dpi0 = fmax(dpi - 10, -pricesi)。

(2)未持有股票。又分为两种情况:之前就没有和今天卖出,对应金额分别为dpi - 11和dpi - 10 + pricesi,则递推公式为dpi1 = fmax(dpi - 11, dpi - 10 + pricesi)。

3.由于最后卖出股票(即未持有股票)的收益一定大于持有股票,所以最后返回结果一定为dppricesSize -- 11

4.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // dp[i][0]:第i天持有股票的最大利润
 3.     // dp[i][1]:第i天不持有股票的最大利润
 4.     int dp[pricesSize][2];
 5.  
 6.     // 第0天持有:只能当天买入,利润为 -股价
 7.     dp[0][0] = -prices[0];
 8.     // 第0天不持有:无操作,利润0
 9.     dp[0][1] = 0;
10.  
11.     for (int i = 1; i < pricesSize; i++){
12.         // 今天持有两种可能:之前就持有 / 今天刚买入
13.         dp[i][0] = fmax(dp[i - 1][0], -prices[i]);
14.         // 今天不持有两种可能:之前就不持有 / 今天卖出昨天持有的
15.         dp[i][1] = fmax(dp[i - 1][1], dp[i - 1][0] + prices[i]);
16.     }
17.  
18.     // 最后一天一定不持有股票利润最大(持有一定亏钱)
19.     return dp[pricesSize - 1][1];
20. }

该算法时间复杂度和空间复杂度均为O(n)。

5.发现递推公式实际上只关心前一项,所以将dp数组的大小减小为dp22来节省空间。完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 滚动dp数组,dp[0]代表前一天状态,dp[1]代表当天状态
 3.     // dp[x][0]:持有股票最大利润
 4.     // dp[x][1]:不持有股票最大利润
 5.     int dp[2][2];
 6.  
 7.     // 初始化第0天(第一天)
 8.     dp[0][0] = -prices[0]; // 当天买入,持有股票
 9.     dp[0][1] = 0;          // 不操作,无股票
10.  
11.     // 从第二天开始遍历
12.     for (int i = 1; i < pricesSize; i++){
13.         // 当天持有:昨天就持有 | 今天第一次买入
14.         dp[1][0] = fmax(dp[0][0], -prices[i]);
15.         // 当天不持有:昨天就无股票 | 昨天持有今天卖出
16.         dp[1][1] = fmax(dp[0][1], dp[0][0] + prices[i]);
17.         // 更新前一天状态为今天,供下一轮循环使用
18.         dp[0][0] = dp[1][0];
19.         dp[0][1] = dp[1][1];
20.     }
21.  
22.     // 最后一天不持有股票为答案
23.     return dp[0][1];
24. }

该算法时间复杂度为O(n),空间复杂度为O(1)。因为每次for循环都会将dp1的值赋给dp0,最后返回的应选择dp01,以防数组长度只有1而导致dp11是垃圾值。

力扣第122题-买卖股票的最佳时机Ⅱ

2026.7.31

1.为了利益最大化,将股票的每日价格看成一个折线图,在低点买入高位卖出就一定能赚钱,实际上就是寻找数组的极值点。使用贪心算法,完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 只有一天股价,无法完成买卖,利润为0
 3.     if (pricesSize == 1) return 0;
 4.  
 5.     // res 累计总利润
 6.     int res = 0;
 7.     // buy 记录买入价格,-1代表当前无持仓
 8.     int buy = -1;
 9.     // 从第二天股价开始遍历
10.     for (int i = 1; i < pricesSize; i++){
11.         // 当前无持仓,且当天股价高于前一天,在前一天买入
12.         if (buy == -1 && prices[i] > prices[i - 1]){
13.             buy = prices[i - 1];
14.         }
15.         // 当前持有股票
16.         if (buy >= 0){
17.             // 当天股价低于前一天,前一天卖出,结算利润,清空持仓
18.             if (prices[i] < prices[i - 1]){
19.                 res += prices[i - 1] - buy;
20.                 buy = -1;
21.             } else if (i == pricesSize - 1){
22.                 // 遍历到最后一天仍持有股票,当天卖出结算利润
23.                 res += prices[i] - buy;
24.                 buy = -1;
25.             }
26.         }
27.     }
28.  
29.     return res;
30. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

2.官方的贪心算法代码写的极其简单,如果第二天比第一天的股票价格贵就买入并卖出。因为题目上强调了可以当天同时买入与卖出,行为上复杂化且等价于寻找波峰波谷,但代码就会变得非常简单:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // res 存放累计的最大利润
 3.     int res = 0;
 4.     // 只有一天价格,无法进行买卖,利润为0
 5.     if (pricesSize == 1) return 0;
 6.     // 从第二天开始遍历所有价格
 7.     for (int i = 1; i < pricesSize; i++){
 8.         // 相邻两天差价为正就累加利润,差价为0或负数不加
 9.         res += fmax(0, prices[i] - prices[i - 1]);
10.     }
11.  
12.     return res;
13. }

3.官方答案还有动态规划算法,看起来比贪心算法要复杂,当下就不再尝试了。

2026.8.7

1.本题相比于力扣第121题-买卖股票的最佳时机,唯一的区别就在于本题能无限次买卖,所以递推公式上有一定区别,主要体现在"当天持有股票"部分,递推公式从dp10 = fmax(dp00, -pricesi)变为了dp10 = fmax(dp00, dp01 - pricesi)。121题只允许买卖一次,如果当天购买股票就说明之前的现金一定是0,所以最后的金额为-pricesi;122题允许买卖多次,之前未持有股票时的现金就应该是dp01 - pricesi

2.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 滚动DP数组,dp[0]代表前一天状态,dp[1]代表当日状态
 3.     // dp[x][0]:持有股票最大利润;dp[x][1]:不持有股票最大利润
 4.     int dp[2][2];
 5.     // 第一天:持股则买入股票,利润-股价;不持股无操作利润0
 6.     dp[0][0] = -prices[0];
 7.     dp[0][1] = 0;
 8.     
 9.     for (int i = 1; i < pricesSize; i++){
10.         // 今日持股:之前就持有 / 昨天空仓今天买入(允许多次买卖)
11.         dp[1][0] = fmax(dp[0][0], dp[0][1] - prices[i]);
12.         // 今日空仓:之前就空仓 / 昨天持股今天卖出
13.         dp[1][1] = fmax(dp[0][1], dp[0][0] + prices[i]);
14.         // 更新前一天状态为当天结果,供下一轮循环使用
15.         dp[0][0] = dp[1][0];
16.         dp[0][1] = dp[1][1];
17.     }
18.     // 最后一天一定不持有股票收益最大,返回结果
19.     return dp[0][1];
20. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

相关推荐
To_OC1 小时前
LC 74 搜索二维矩阵:换皮的二分查找,我居然一开始没看出来
javascript·算法·leetcode
文心快码BaiduComate1 小时前
文心快码荣获“中国优秀软件产品”,实力再获国家级认可
算法·代码规范
玖玥拾1 小时前
LeetCode 189 轮转数组
算法·leetcode
ZhangShao06071 小时前
题解:CF2249B
c++·算法
小玮看世界1 小时前
[Python]线段树与二分法
数据结构·算法
摇滚侠2 小时前
SpringBoot 官网 阅读笔记 启用生产就绪功能 端点
spring boot·笔记·后端
AI云海3 小时前
完整机器学习工业项目流水线笔记
人工智能·笔记·机器学习
2601_965799683 小时前
2026 在线笔试平台深度测评与选型指南:从功能、稳定性、AI能力、防作弊全面分析
人工智能·笔记·功能测试
xqqxqxxq3 小时前
SQL 连接查询技术笔记
数据库·笔记·sql