力扣股票系列题目汇总
贪心:
- 121:最多进行一次买卖
- 122:可以多次买卖
DP:
- 309:多次买卖+卖出冷冻期
- 714:多次买卖+卖出手续费
- 123:最多两次买卖
- 188:最多 k 次买卖
121 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
思路:贪心
要顺序地找到数组最小的元素,然后找到最大的元素,差值即为最大利润
代码
//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int buy = INT_MAX, sell = INT_MIN;
for (int& price : prices){
// 记录历史最低价格
buy = min(buy, price);
// 记录历史最大收益
sell = max(sell, price - buy);
}
return sell;
}
};
122 买卖股票的最佳时机 II
给定一个数组 prices ,其中 prices[i] 表示股票第 i 天的价格。
在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
思路:贪心
只要比前一天价格高,就卖出;卖出的同时买入,因为第二天有个min函数决定是否真的买入
代码
//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
// 只要比前一天高,就卖出
int maxProfit(vector<int>& prices) {
int buy = INT_MAX, profit = 0;
for (int i = 0; i < prices.size(); ++i){
buy = min(buy, prices[i]);
if (prices[i] > buy) {
profit += prices[i] - buy;
buy = prices[i];
}
}
return profit;
}
};
优化
发现buy
和 prices[i]
的大小比较进行了两次,所以将循环体内的min函数去掉。
//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int profit = 0;
for (int i = 1; i < prices.size(); ++i){
if (prices[i] > prices[i-1]) {
profit += prices[i] - prices[i-1];
}
}
return profit;
}
};
309. 最佳买卖股票时机含冷冻期
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
思路:状态机
维护四个数组,代表当天结束时,采用这种行为后,目前的收益
画出状态机(状态转移方程):
代码
//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<int> buy(n), sell(n), s1(n), s2(n);
buy[0] = s1[0] = - prices[0];
s2[0] = sell[0] = 0;
for (int i = 1; i < n; ++i){
buy[i] = s2[i-1] - prices[i];
s1[i] = max(s1[i-1], buy[i-1]);
sell[i] = max(buy[i-1] + prices[i], s1[i-1] + prices[i]);
s2[i] = max(s2[i-1], sell[i-1]);
}
return max(s2[n-1], sell[n-1]);
}
};
714. 买卖股票的最佳时机含手续费
给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
思路:状态机
考虑到交易费,不能无限次地买卖,每次想买入的时候都需要斟酌一下。所以每次sell之后,还可能是sell,即不买。由于没有冷冻期了,状态可以自旋了。
状态图:
代码
//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
vector<int> buy(n), sell(n);
buy[0] = -prices[0], sell[0] = 0;
for (int i = 1;i < n; ++i){
buy[i] = max(buy[i-1], sell[i-1] - prices[i]);
sell[i] = max(sell[i-1], buy[i-1] + prices[i] - fee);
}
return sell[n-1];
}
};
123. 买卖股票的最佳时机 III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
思路
每次买卖都设置状态,因为不含冷冻期,状态还是可以自旋。
状态机:
代码
//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<int> buy1(n), sell1(n), buy2(n), sell2(n);
//buy2[0]不知道是多少,设置为最小值
buy1[0] = - prices[0], buy2[0] = INT_MIN, sell1[0] = sell2[0] = 0;
for (int i = 1; i < n; ++i){
buy1[i] = max(buy1[i-1], -prices[i]);
sell1[i] = max(sell1[i-1], buy1[i-1] + prices[i]);
buy2[i] = max(buy2[i-1], sell1[i-1] - prices[i]);
sell2[i] = max(sell2[i-1], buy2[i-1] + prices[i]);
}
//可能完成一笔交易就是利润最大的
return max(sell1[n-1], sell2[n-1]);
}
};
优化
由于每次状态转移时,dp数组不是全部都用上了,可以使用经典的DP空间优化术
//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 1) return 0;
int buy1, buy2, sell1, sell2, prev_buy1 = - prices[0], prev_buy2 = INT_MIN, prev_sell1 = 0, prev_sell2 = 0;
for (int i = 1; i < n; ++i){
buy1 = max(prev_buy1, -prices[i]);
sell1 = max(prev_sell1, buy1 + prices[i]);
buy2 = max(prev_buy2, sell1 - prices[i]);
sell2 = max(prev_sell2, buy2 + prices[i]);
prev_buy1 = buy1;
prev_buy2 = buy2;
prev_sell1 = sell1;
prev_sell2 = sell2;
}
return max(sell1, sell2);
}
};
188. 买卖股票的最佳时机 IV
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
思路
根据123优化后的代码,把buy
、 sell
装进一个数组里面,例如buy = [buy0, buy1, ....]
代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
if (n <= 1 or k == 0) return 0;
vector<int> buy(k), sell(k), prev_buy(k, INT_MIN), prev_sell(k);
prev_buy[0] = -prices[0];
for (int i = 1; i < n; ++i){
for (int j = 0; j < k; ++j){
if (j) buy[j] = max(prev_buy[j], sell[j-1]-prices[i]);
else buy[j] = max(prev_buy[j], -prices[i]);
sell[j] = max(prev_sell[j], buy[j] + prices[i]);
prev_buy[j] = buy[j];
prev_sell[j] = sell[j];
}
}
return *max_element(sell.begin(), sell.end());;
}
};
总结
有冷冻期:状态机中增加状态
交易次数限制:状态不复用