股票买卖的最佳时机问题是一个经典的算法问题,通常指在给定的股票价格数组中,找到能够实现最大利润的买入和卖出时机。
问题可以有不同的变体,以下分别介绍 简单版本 和 高级版本 的解法,并附上实现代码。
简单版本:只允许一次买入和一次卖出
问题描述
给定一个数组 prices,其中 prices[i] 表示第 i 天的股票价格。你只能完成一次交易(一次买入和一次卖出),求最大利润。如果无法获得利润,则返回 0。
思路
- 遍历数组,记录历史最低点
minPrice。 - 同时计算每一天如果卖出能获得的最大利润
maxProfit: profit=prices[i]−minPrice - 最后返回最大利润。
时间复杂度
- 时间复杂度: O(n)O(n)O(n),只需一次遍历。
- 空间复杂度: O(1)O(1)O(1),仅需几个变量存储中间值。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
int maxProfit(const std::vector<int>& prices) {
if (prices.empty()) return 0;
int minPrice = prices[0];
int maxProfit = 0;
for (size_t i = 1; i < prices.size(); ++i) {
// 更新最低价格
minPrice = std::min(minPrice, prices[i]);
// 计算最大利润
maxProfit = std::max(maxProfit, prices[i] - minPrice);
}
return maxProfit;
}
int main() {
std::vector<int> prices = {7, 1, 5, 3, 6, 4};
std::cout << "Max Profit: " << maxProfit(prices) << std::endl;
return 0;
}
高级版本:允许多次买入和卖出
问题描述
股票价格数组 prices 给定,允许多次买入和卖出股票,但手上最多只能持有一股。求实现最大利润。
思路
- 在价格上升的区间内,选择每天卖出(贪心算法)。
- 累加所有上升区间的利润,即: profit+=max(0,prices[i+1]−prices[i])
时间复杂度
- 时间复杂度: O(n)O(n)O(n),只需一次遍历。
- 空间复杂度: O(1)O(1)O(1)。
代码实现
#include <iostream>
#include <vector>
int maxProfitMultipleTransactions(const std::vector<int>& prices) {
if (prices.empty()) return 0;
int maxProfit = 0;
for (size_t i = 1; i < prices.size(); ++i) {
// 累加所有上升区间的利润
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
int main() {
std::vector<int> prices = {7, 1, 5, 3, 6, 4};
std::cout << "Max Profit with Multiple Transactions: "
<< maxProfitMultipleTransactions(prices) << std::endl;
return 0;
}
进阶版本:最多两次交易
问题描述
给定股票价格数组 prices,最多只允许完成两次交易(两次买入和卖出)。求最大利润。
思路
-
将问题分解为两个子问题:
- 第一次交易的最大利润。
- 第二次交易的最大利润(在第一次交易结束后的子数组上计算)。
-
通过动态规划的方法,分别从左到右和从右到左遍历数组:
leftProfit[i]: 第i天之前(包括第i天)的最大利润。rightProfit[i]: 第i天之后(包括第i天)的最大利润。
-
最后结果为: maxProfit=max(leftProfit[i]+rightProfit[i])
时间复杂度
- 时间复杂度: O(n)O(n)O(n),两次遍历。
- 空间复杂度: O(n)O(n)O(n),需要两个数组存储部分利润。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
int maxProfitTwoTransactions(const std::vector<int>& prices) {
if (prices.empty()) return 0;
int n = prices.size();
std::vector<int> leftProfit(n, 0);
std::vector<int> rightProfit(n, 0);
// 从左到右计算第一次交易的最大利润
int minPrice = prices[0];
for (int i = 1; i < n; ++i) {
minPrice = std::min(minPrice, prices[i]);
leftProfit[i] = std::max(leftProfit[i - 1], prices[i] - minPrice);
}
// 从右到左计算第二次交易的最大利润
int maxPrice = prices[n - 1];
for (int i = n - 2; i >= 0; --i) {
maxPrice = std::max(maxPrice, prices[i]);
rightProfit[i] = std::max(rightProfit[i + 1], maxPrice - prices[i]);
}
// 合并两次交易的利润
int maxProfit = 0;
for (int i = 0; i < n; ++i) {
maxProfit = std::max(maxProfit, leftProfit[i] + rightProfit[i]);
}
return maxProfit;
}
int main() {
std::vector<int> prices = {3, 3, 5, 0, 0, 3, 1, 4};
std::cout << "Max Profit with Two Transactions: "
<< maxProfitTwoTransactions(prices) << std::endl;
return 0;
}
总结
- 一次交易问题: 使用线性扫描即可解决,时间复杂度 O(n)O(n)O(n)。
- 多次交易问题: 使用贪心策略,累加所有上升区间的利润。
- 两次交易问题: 使用动态规划分别计算左右最大利润,然后合并结果。
根据实际应用场景选择合适的算法解决股票买卖问题!