股票买卖的最佳时机 | 豆包MarsCode AI刷题

219 阅读3分钟

股票买卖的最佳时机问题是一个经典的算法问题,通常指在给定的股票价格数组中,找到能够实现最大利润的买入和卖出时机。

问题可以有不同的变体,以下分别介绍 简单版本高级版本 的解法,并附上实现代码。

简单版本:只允许一次买入和一次卖出

问题描述

给定一个数组 prices,其中 prices[i] 表示第 i 天的股票价格。你只能完成一次交易(一次买入和一次卖出),求最大利润。如果无法获得利润,则返回 0。

思路

  1. 遍历数组,记录历史最低点 minPrice
  2. 同时计算每一天如果卖出能获得的最大利润 maxProfit: profit=prices[i]−minPrice
  3. 最后返回最大利润。

时间复杂度

  • 时间复杂度: 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 给定,允许多次买入和卖出股票,但手上最多只能持有一股。求实现最大利润。

思路

  1. 在价格上升的区间内,选择每天卖出(贪心算法)。
  2. 累加所有上升区间的利润,即: 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,最多只允许完成两次交易(两次买入和卖出)。求最大利润。

思路

  1. 将问题分解为两个子问题:

    • 第一次交易的最大利润。
    • 第二次交易的最大利润(在第一次交易结束后的子数组上计算)。
  2. 通过动态规划的方法,分别从左到右和从右到左遍历数组:

    • leftProfit[i]: 第 i 天之前(包括第 i 天)的最大利润。
    • rightProfit[i]: 第 i 天之后(包括第 i 天)的最大利润。
  3. 最后结果为: 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;
}

总结

  1. 一次交易问题: 使用线性扫描即可解决,时间复杂度 O(n)O(n)O(n)。
  2. 多次交易问题: 使用贪心策略,累加所有上升区间的利润。
  3. 两次交易问题: 使用动态规划分别计算左右最大利润,然后合并结果。

根据实际应用场景选择合适的算法解决股票买卖问题!