Leetcode121 Best Time to Buy and Sell Stock (至多一笔交易)
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)
详细题解: https://www.acwing.com/solution/LeetCode/content/214/
题目描述
假设你有一个数组,其中第i个元素表示第i天某个股票的价格。
如果您只允许完成**至多一笔交易**(即买入一只股票并卖出一只股票),则设计一种算法以找到最大利润。
必须先购买股票再出售股票。
样例
Example 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第二天买(price = 1) ,在第五天卖 (price = 6), 利润 = 6-1 = 5.
思路:
贪心算法
扫描一遍,每次更新最大利润和最低价格即可。
代码:
int maxProfit(vector<int>& prices) {
if (!prices.size()) return 0;
int len = prices.size();
int minv = INT_MAX;
int res = 0;
for (int i = 0; i < len; i++){
res = max(res, prices[i] - minv);
minv = min(minv, prices[i]);
}
return res;
}
LeetCode 122. Best Time to Buy and Sell Stock II (不限次交易)
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)
详细题解: https://www.acwing.com/solution/LeetCode/content/261/
假设你有一个数组,其中第i个元素表示第i天某个股票的价格。
设计一种算法以找到最大利润,可以完成**任意多次交易**,但必须先购买股票再出售股票,不能同时多次交易。
样例
Example 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 第二天买(price = 1),第三天卖(price=5),利润为4;
第四天买(price = 3),第五天卖(price=6),利润为3。
思路1:
贪心算法
遍历一次数组,低进高出,把所有上升区间的价格差相加起来就是最终利润。
可以拆分成以下几种情况,
递增,如[1,2,3],那么1买3卖 与 每天都买入卖出 等价
递减,如[3,2,1],赚钱是赚不了的
先高再低,如[1,3,2],那么只能在1买3卖捞一笔
先低再高,如[2,1,3],那么同样只能在1买3卖捞一笔
短数组的情况可以推广到所有数组。
代码:
int maxProfit(vector<int>& prices) {
int ans = 0;
int len = prices.size();
for (int i = 1; i < len; i++){
if (prices[i] > prices[i - 1])
ans += prices[i] - prices[i - 1];
}
return ans;
}
思路2:
动态规划
记录两个状态: g[i]表示第i天,当前不持有股票的最大收益;f[i]表示第i天,当前持有股票的最大收益。
状态转移为:
g[i] = max(g[i - 1], f[i - 1] + prices[i]),
f[i] = max(f[i - 1], g[i - 1] - prices[i]).
初始化: f = INT_MIN, g = 0 //初始化要仔细考虑
最终答案为f[n - 1],即最后一天不持有股票的最大收益。
优化
注意到状态转移之和前一层有关,故可以优化掉第一维。
每次提前取出前一层的值,用其更新为新的值即可。
代码:
int maxProfit(vector<int>& prices) {
if(!prices.size()) return 0;
int len = prices.size();
int f = INT_MIN; // 持股 //初始化要仔细考虑一下
int g = 0; // 不持股
for(int i = 0; i < len; i++){
int newf = max(f, g - prices[i]);
int newg = max(g, f + prices[i]);
f = newf;
g = newg;
}
return g;
}
LeetCode 123. Best Time to Buy and Sell Stock III (最多两次交易)
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/)
详细题解: https://www.acwing.com/solution/LeetCode/content/219/
题目描述
假设你有一个数组,其中第i个元素表示第i天某个股票的价格。
设计一种算法以找到最大利润,可以完成**最多两次交易**(一买一卖算一次交易),但必须先购买股票再出售股票,不能同时多次交易。
样例
Example 1:
输入: [3,3,5,0,0,3,1,4]
输出: 7
解释: 第四天买(price = 0),第六天卖(price=3),利润为3;
第七天买(price = 1),第八天卖(price=4),利润为3。
思路:
算法1 动态规
在整个区间的每一点切开, 然后分别计算左子区间和右子区间的最大值,然后再用O(n)时间找到整个区间的最大值。
遍历一遍数组,求[0,i−1][0,i−1]区间的最大利润f(i)f(i),具体做法是找当前最低价格low,判断是要以low买入当天卖出,还是不动
从后往前遍历,求[i,n−1][i,n−1]区间的最大利润g(i)g(i),具体做法是找当前最高价格high,判断是要当天买入以high卖出,还是不动
遍历,求最大利润max(f(i)+g(i))max(f(i)+g(i))
代码:
int maxProfit(vector<int>& prices) {
if (!prices.size()) return 0;
int len = prices.size();
vector<int> f(len, 0);
vector<int> g(len, 0);
int minv = INT_MAX;
for (int i = 1; i < len; i++){
f[i] = max(f[i - 1], prices[i - 1] - minv); // 这里一次max就可以 取从前往后 f 的最大值
minv = min(minv, prices[i - 1]);
}
int maxv = prices[len - 1];
for (int i = len - 2; i >= 0; i--){ // 注意边界
g[i] = max(max(0, maxv - prices[i]), g[i + 1]); //两次max嗷 取从后往前 g 的最大值
maxv = max(maxv, prices[i]);
}
int res = 0;
for (int i = 0; i < len; i++){
res = max(res, f[i] + g[i]);
}
return res;
}