[算法] 买卖股票的最佳时机
“买卖股票的最佳时机” 是 LeetCode 上的一系列题目,包含 6 道题 (1~4、含手续费、含冷冻期),属于比较常考的面试题。输入给定了一段连续时间内每一天的股票价格,每道题对股票的交易做出了各种限制,求能够获取的最大利润。
- 不能当天买入当天卖出 (相当于不交易)
- 不能同时参与多笔交易,再次买入前需要卖出之前的股票
题目 | 难度 | 交易次数 | 其他约束 | 算法 |
---|---|---|---|---|
买卖股票的最佳时机 | 简单 | 最多交易 1 次 | 前缀最值 | |
买卖股票的最佳时机 II | 中等 | 交易次数不限 | 贪心 | |
买卖股票的最佳时机 III | 困难 | 最多交易 2 次 | 动态规划 | |
买卖股票的最佳时机 IV | 困难 | 最多交易 k 次 | 动态规划 | |
最佳买卖股票时机含冷冻期 | 中等 | 交易次数不限 | 卖出股票后有 1 天冷冻期 | 动态规划 |
买卖股票的最佳时机含手续费 | 中等 | 交易次数不限 | 每次交易有手续费 | 动态规划 |
1. 买卖股票的最佳时机
由于只能买卖一次,对于第 i 天买入的股票,只需要在后续时间内股票价格最高时卖出即可。为了降低时间复杂度,可以从左往右遍历,记录目前为止最低的股票价格作为买入价格,依次计算如果今天卖出能得到的利润,求最大值就得到了题目要求的结果。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minPrice = INT_MAX;
int res = 0;
for (int price : prices) {
res = max(res, price - minPrice);
minPrice = min(minPrice, price);
}
return res;
}
};
- 时间复杂度:
- 空间复杂度:
2. 买卖股票的最佳时机 II
由于不限制交易次数,可以每隔一天就尝试进行一次交易,如果相邻两天股票涨了,就进行一次交易。这种交易方式获得的利润是最高的,因为避免了所有的股票价格下跌。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int ans = 0;
for (int i = 0; i < n - 1; ++i)
if (prices[i + 1] > prices[i])
ans += prices[i + 1] - prices[i];
return ans;
}
};
- 时间复杂度:
- 空间复杂度:
3. 买卖股票的最佳时机 III
没有容易想到的解题思路时可以尝试用动态规划,每天记录 4 个状态:第 1 次买入的利润 buy1,第 1 次卖出的利润 sell1,第 2 次买入的利润 buy2,第 2 次卖出的利润 sell2。买入利润可以理解为买入股票价格的负值,计算卖出利润时再加上卖出股票的价格,每个状态在前一个状态的基础上进行更新。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int buy1 = -prices[0], sell1 = 0;
int buy2 = -prices[0], sell2 = 0;
for (int i = 1; i < n; ++i) {
buy1 = max(buy1, -prices[i]);
sell1 = max(sell1, buy1 + prices[i]);
buy2 = max(buy2, sell1 - prices[i]);
sell2 = max(sell2, buy2 + parices[i]);
}
return sell2;
}
};
- 时间复杂度:
- 空间复杂度:
4. 买卖股票的最佳时机 IV
题目背景跟 III 很像,解法也类似,每天记录 2*(k+1) 个状态,k 为最大交易次数,表示这一天第 0~k 次买入和卖出的利润。定义第 0 次交易是为了顺利计算 buy[1],只会用到 sell[0] = 0。长度为 n 的数组最多有 n/2 次有效交易,因此可以在计算前尝试缩小 k 的值。
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
k = min(k, n / 2);
vector<int> buy(k + 1), sell(k + 1);
sell[0] = 0;
for (int i = 1; i <= k; ++i) {
buy[i] = INT_MIN / 2;
sell[i] = INT_MIN / 2;
}
for (int i = 0; i < n; ++i) {
for (int j = 1; j <= k; ++j) {
buy[j] = max(buy[j], sell[j-1] - prices[i]);
sell[j] = max(sell[j], buy[j] + prices[i]);
}
}
return *max_element(sell.begin(), sell.end());
}
};
- 时间复杂度:
- 空间复杂度:
5. 最佳买卖股票时机含冷冻期
这道题的解法也可以类比 III,每天记录 3 个状态:当天买入的利润,当天卖出的利润,当天什么都不干的利润。买入利润需要从前一天什么都不干的利润转移 (确保不在冷冻期),什么都不干的利润从前一天卖出的利润转移。最终的最大利润为最后一天卖出的利润和什么都不干的利润的最大值,因为前期卖出的最大利润可以通过什么都不干的利润转移到最后一天。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int buy = -prices[0], sell = 0, hot = 0;
for (int i = 1; i < n; ++i) {
int buy2 = max(buy, hot - prices[i]);
int sell2 = buy + prices[i];
int hot2 = max(hot, sell);
buy = buy2;
sell = sell2;
hot = hot2;
}
return max(sell, hot);
}
};
- 时间复杂度:
- 空间复杂度:
6. 买卖股票的最佳时机含手续费
这道题跟 ”含冷冻期“ 很像,不限制交易次数,但是不能用贪心,因为长期的收益有可能高于手续费。每天记录 2 个状态:当前买入的利润,当天卖出的利润。计算卖出利润时需要减去手续费。
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
int buy = -prices[0], sell = 0;
for (int i = 1; i < n; ++i) {
int buy2 = max(buy, sell - prices[i]);
int sell2 = max(sell, buy + prices[i] - fee);
buy = buy2;
sell = sell2;
}
return sell;
}
};
- 时间复杂度:
- 空间复杂度:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术