代码随想录算法训练营Day49|121. 买卖股票的最佳时机、122.买卖股票的最佳时机II
目录
121. 买卖股票的最佳时机
方法一:暴力解法
方法二:贪心
方法三:动态规划
算法实现
122.买卖股票的最佳时机II
前言
思路
算法实现
总结:
121. 买卖股票的最佳时机
题目链接
文章链接
方法一:暴力解法
class Solution {
public:
int maxProfit(vector<int>& prices) {
int max = 0;
for (int i = 0; i < prices.size() - 1; i++) {
for (int j = i + 1; j < prices.size(); j++) {
if (prices[j] - prices[i] > max) max = prices[j] - prices[i];
}
}
return max;
}
};
暴力解法的思路较为简单,但是提交超时了;
方法二:贪心
class Solution {
public:
int maxProfit(vector<int>& prices) {
int low = INT_MAX;
int result = 0;
for (int i = 0; i < prices.size(); i++) {
low = min(low, prices[i]);
// 可以保证最大值一定在最小值右侧
result = max(result, prices[i] - low);
}
return result;
}
};
贪心的思路,股票只买卖一次,取左值最小值,取右值最大值,得到的差值为最大利润。
方法三:动态规划
利用动规五部曲进行分析:
1.确定dp数组及其下标含义:
dp[i][0]表示第i天持有股票所拥有的最大现金,一开始现金是0,那么加入第i天买入股票现金就是 -prices[i], 是一个负数。(此处的持有不是指当天买入,可能是前面几天买入的股票);
dp[i][1]表示第i天不持有股票所拥有的最大现金;
2.确定递推公式:
如果第i天持有股票即dp[i][0], 那么可以由两个可能来源
- 第i - 1天就持有股票,没有卖出,此时dp[i][0] = dp[i - 1][0];
- 第i - 1天没有持有股票,而是在第i天买入,此时dp[i][0] = -prices[i]
那么dp[i] 应该选择两种情况中的最大值:dp[i][0] = max(dp[i - 1][0], -prices[i]);
如果第i天未持有股票即dp[i][1],同样可能有两个来源:
- 第i - 1天就未持有股票即dp[i - 1][1]:dp[i][1] = dp[i - 1][1];
- 第i - 1天还持有股票,在第i天卖出:dp[i][1] = dp[i - 1][0] + prices[i];
dp[i]依然是取两种情况中的最大值:dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]).
3.dp数组初始化:
一开始现金是0,在第一天买入股票(持有股票)现金就是-prices[0],即dp[0][0] = -prices[0],第一天未持有股票所拥有的现金就是0,即dp[0][1] = 0;
4.确定遍历顺序:
从递推公式可以看出dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历。
5.打印dp数组:
以示例1,输入:[7,1,5,3,6,4]为例,dp数组状态如下:
dp[5][1]就是最终结果,因为同一天不持有股票时的钱一定比持有股票时的钱多。
算法实现
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
vector<vector<int>> dp(len, vector<int>(2));
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++) {
dp[i][0] = max(dp[i - 1][0], -prices[i]);
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
}
return dp[len - 1][1];
}
};
从递推公式可以看出,dp[i]只是依赖于dp[i - 1]的状态,可以对以上算法进行优化,使用滚动数组来节省空间。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
vector<vector<int>> dp(2, vector<int>(2));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]);
dp[i % 2][1] = max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]);
}
return dp[(len - 1) % 2][1];
}
};
122.买卖股票的最佳时机II
题目链接
文章链接
前言
前一题买卖股票问题只能买卖一次股票,而本题没有一次的限制,虽然每次最多只能仍然只能持有一张股票,但是可以多次买入卖出股票。
思路
本题与上一题的不同主要在于推导dp[i][0]时,如果第i天持有股票,第一种情况同样是第i - 1天就持有了股票,dp[i][0] = dp[i - 1][0];而第二种情况则是第i - 1天没有持有股票,在第i天购入股票,此时不再是0 - prices[i],因为当前的现金不一定是0,前面可能已经经过了好几次的股票买入和卖出操作,因此此时的dp[i][0] = dp[i - 1][1] + prices[i];
未持有股票的情况dp[i][1]与上题相同,依然是dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i])。
算法实现
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
vector<vector<int>> dp(len, vector<int>(2));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; 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]);
}
return dp[len - 1][1];
}
};
同样也可以利用滚动数组进行优化,代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
vector<vector<int>> dp(2, vector<int>(2));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);
dp[i % 2][1] = max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]);
}
return dp[(len - 1)% 2][1];
}
};
总结:
今天学了买卖股票类的问题,初步简单掌握。