力扣第123题 买卖股票的最佳时机 III c++ 动态规划解法 hard题 附Java代码
题目
困难
相关标签
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4] 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1] 输出:0
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 105
思路和解题方法
创建一个二维数组
dp
,用于存储每一天进行不同次数交易时的最大收益。数组的行代表天数,列代表交易次数。初始时,数组中的所有元素都设置为0。设置初始状态,即第一天进行第一次交易和第二次交易时的买入价格。这里通过将
dp[0][1]
和dp[0][3]
分别设为-prices[0]
来表示。进入循环遍历,从第二天开始计算每一天不同交易次数的最大收益。
对于每一天的交易次数,分别计算四种情况下的最大收益。
a. 第i天没有进行交易时的最大收益与前一天相同,即
dp[i][0] = dp[i-1][0]
。b. 第i天进行第一次交易时的最大收益为前一天进行第一次交易的最大收益或者前一天没有交易并买入股票后的收益中的较大值。即
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
。c. 第i天进行第一次交易后卖出股票时的最大收益为前一天进行第一次交易并卖出股票的最大收益或者前一天进行第一次交易后持有股票并卖出后的收益中的较大值。即
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
。d. 第i天进行第二次交易时的最大收益为前一天进行第二次交易的最大收益或者前一天没有交易并买入股票后的收益中的较大值。即
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i])
。e. 第i天进行第二次交易后卖出股票时的最大收益为前一天进行第二次交易并卖出股票的最大收益或者前一天进行第二次交易后持有股票并卖出后的收益中的较大值。即
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
。循环遍历结束后,返回数组中最后一天进行第二次交易后的最大收益,即
dp[num-1][4]
。
复杂度
时间复杂度:
O(N)
时间复杂度:O(N),其中 N 是股票价格数组的长度。我们只需要遍历一次数组即可计算出最大收益。
空间复杂度
O(N)
空间复杂度:O(N),我们使用了一个二维数组 dp,其中第一维的长度为 N,第二维的长度为 5,因此空间复杂度为 O(N*5)=O(N)。
c++ 代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int num = prices.size(); // 获取输入数组的长度
vector<vector<int>> dp(num, vector<int>(5, 0)); // 创建一个二维数组,dp[i][j]表示第i天进行第j次交易时的最大收益
dp[0][1] = -prices[0]; // 初始状态,第一天进行第一次交易时的最大收益为买入第一天的股票价格
dp[0][3] = -prices[0]; // 初始状态,第一天进行第二次交易时的最大收益为买入第一天的股票价格
for (int i = 1; i < num; i++) {
dp[i][0] = dp[i-1][0]; // 第i天没有进行交易时的最大收益与前一天相同
// 第i天进行第一次交易时的最大收益为前一天进行第一次交易的最大收益或者前一天没有交易并买入股票后的收益中的较大值
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
// 第i天进行第一次交易后卖出股票时的最大收益为前一天进行第一次交易并卖出股票的最大收益或者前一天进行第一次交易后持有股票并卖出后的收益中的较大值
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]);
// 第i天进行第二次交易时的最大收益为前一天进行第二次交易的最大收益或者前一天没有交易并买入股票后的收益中的较大值
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]);
// 第i天进行第二次交易后卖出股票时的最大收益为前一天进行第二次交易并卖出股票的最大收益或者前一天进行第二次交易后持有股票并卖出后的收益中的较大值
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]);
}
return dp[num-1][4]; // 返回最后一天进行第二次交易后的最大收益
}
};
优化空间
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0; // 如果输入数组为空,则返回0
vector<int> dp(5, 0); // 创建一个长度为5的一维vector,初始值都为0
dp[1] = -prices[0]; // 第一天持有股票时的最大收益为买入第一天的股票价格
dp[3] = -prices[0]; // 第一天持有股票时的最大收益为买入第一天的股票价格
for (int i = 1; i < prices.size(); i++) { // 遍历每一天的状态
dp[1] = max(dp[1], dp[0] - prices[i]); // 第i天持有股票时的最大收益等于前一天持有股票时的最大收益,或者前一天未持有股票且购入了当天的股票后的最大收益
dp[2] = max(dp[2], dp[1] + prices[i]); // 第i天未持有股票且做了一次买卖操作后的最大收益等于前一天未持有股票且做了一次买卖操作的最大收益,或者前一天持有股票且卖出股票后的最大收益
dp[3] = max(dp[3], dp[2] - prices[i]); // 第i天持有股票且做了一次买卖操作后的最大收益等于前一天持有股票且做了一次买卖操作的最大收益,或者前一天未持有股票且购入了当天的股票后的最大收益
dp[4] = max(dp[4], dp[3] + prices[i]); // 第i天未持有股票且做了两次买卖操作后的最大收益等于前一天未持有股票且做了两次买卖操作的最大收益,或者前一天持有股票且卖出股票后且做了一次买卖操作后的最大收益
}
return dp[4]; // 返回最后一天未持有股票且做了两次买卖操作的最大收益
}
};
附Java代码
//一 :空间为 N
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
// 边界判断, 题目中 length >= 1, 所以可省去
if (prices.length == 0) return 0;
/*
* 定义 5 种状态:
* 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出
*/
int[][] dp = new int[len][5];
dp[0][1] = -prices[0];
// 初始化第二次买入的状态是确保 最后结果是最多两次买卖的最大利润
dp[0][3] = -prices[0];
for (int i = 1; i < len; i++) {
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
}
return dp[len - 1][4];
}
}
// 版本二: 空间优化
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[4];
// 存储两次交易的状态就行了
// dp[0]代表第一次交易的买入
dp[0] = -prices[0];
// dp[1]代表第一次交易的卖出
dp[1] = 0;
// dp[2]代表第二次交易的买入
dp[2] = -prices[0];
// dp[3]代表第二次交易的卖出
dp[3] = 0;
for(int i = 1; i <= prices.length; i++){
// 要么保持不变,要么没有就买,有了就卖
dp[0] = Math.max(dp[0], -prices[i-1]);
dp[1] = Math.max(dp[1], dp[0]+prices[i-1]);
// 这已经是第二次交易了,所以得加上前一次交易卖出去的收获
dp[2] = Math.max(dp[2], dp[1]-prices[i-1]);
dp[3] = Math.max(dp[3], dp[2]+ prices[i-1]);
}
return dp[3];
}
}
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)