买卖股票的最佳时机全系列核心推演

专题核心思想

股票买卖系列是动态规划中“状态机模型”的经典代表。其本质是将每一天的决策抽象为若干相互转换的状态(如“持有股票”、“不持有股票”、“处于冷冻期”等),进而写出清晰的状态转移方程,并利用滚动变量实现 的空间压缩。


一、LeetCode 121. 买卖股票的最佳时机(一次交易)

LeetCode 121. Best Time to Buy and Sell Stock · 难度:简单

1. 核心思路

只允许买入一次并卖出一次。遍历价格数组,维护历史最低买入成本 buy;每天如果卖出,潜在收益为 price - buy,更新全局最大利润 maxProfit 即可。

2. 代码实现

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
  let minPrice = prices[0];
  let max = 0;
 
  for (let i = 1; i < prices.length; i++) {
    if (prices[i] < minPrice) {
      minPrice = prices[i];
    } else {
      max = Math.max(max, prices[i] - minPrice);
    }
  }
 
  return max;
};
  • 时间复杂度
  • 空间复杂度

二、LeetCode 122. 买卖股票的最佳时机 II(无限次交易)

LeetCode 122. Best Time to Buy and Sell Stock II · 难度:简单

1. 核心思路

可以进行无限次买卖,但同一时刻最多只能持有一支股票。

  • 贪心视角:由于交易无手续费,每一段跨天上涨()的差价都可以拆解为单日的正收益累加;
  • 数学等价:只要 ,就纳入总收益。

2. 代码实现

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
  let profit = 0;
  for (let i = 1; i < prices.length; i++) {
    const diff = prices[i] - prices[i - 1];
    if (diff > 0) {
      profit += diff;
    }
  }
  return profit;
};
  • 时间复杂度
  • 空间复杂度

三、LeetCode 309. 买卖股票的最佳时机含冷冻期

LeetCode 309. Best Time to Buy and Sell Stock with Cooldown · 难度:中等

1. 核心思路(三状态机)

卖出股票后次日无法买入(1 天冷冻期)。定义第 天结束时的 3 种互斥状态:

  1. dp[0]:当前持有股票时的最大收益;
  2. dp[1]:当前不持有股票且处于冷冻期(即今天刚卖出);
  3. dp[2]:当前不持有股票且不在冷冻期(可以随时买入)。

状态转移方程

2. 代码实现

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
  if (prices.length <= 1) return 0;
 
  // 初始状态:持有 / 刚卖出(冷冻) / 自由状态
  let [hold, cooldown, free] = [-prices[0], 0, 0];
 
  for (let i = 1; i < prices.length; i++) {
    const cur = prices[i];
    const newHold = Math.max(hold, free - cur);
    const newCooldown = hold + cur;
    const newFree = Math.max(free, cooldown);
 
    hold = newHold;
    cooldown = newCooldown;
    free = newFree;
  }
 
  return Math.max(cooldown, free);
};
  • 时间复杂度
  • 空间复杂度

四、LeetCode 714. 买卖股票的最佳时机含手续费

LeetCode 714. Best Time to Buy and Sell Stock with Transaction Fee · 难度:中等

1. 核心思路

每笔交易都需要支付一笔固定手续费 fee。 定义状态:

  • hold:持有股票的最大收益;
  • cash:未持有股票的最大收益(在卖出时扣除手续费)。

转移方程

2. 代码实现

/**
 * @param {number[]} prices
 * @param {number} fee
 * @return {number}
 */
var maxProfit = function(prices, fee) {
  let hold = -prices[0];
  let cash = 0;
 
  for (let i = 1; i < prices.length; i++) {
    cash = Math.max(cash, hold + prices[i] - fee);
    hold = Math.max(hold, cash - prices[i]);
  }
 
  return cash;
};
  • 时间复杂度
  • 空间复杂度

五、LeetCode 123. 买卖股票的最佳时机 III(最多两次交易)

LeetCode 123. Best Time to Buy and Sell Stock III · 难度:困难

1. 核心思路(四状态推导)

任意一天结束时,至多经历 4 个关键节点:

  1. buy1:第一次买入后持有的最大收益;
  2. sell1:第一次卖出后不持有的最大收益;
  3. buy2:第二次买入后持有的最大收益;
  4. sell2:第二次卖出后不持有的最大收益。

2. 代码实现

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
  let buy1 = -prices[0];
  let sell1 = 0;
  let buy2 = -prices[0];
  let sell2 = 0;
 
  for (let i = 1; i < prices.length; i++) {
    const cur = prices[i];
    buy1 = Math.max(buy1, -cur);
    sell1 = Math.max(sell1, buy1 + cur);
    buy2 = Math.max(buy2, sell1 - cur);
    sell2 = Math.max(sell2, buy2 + cur);
  }
 
  return sell2;
};
  • 时间复杂度
  • 空间复杂度

六、LeetCode 188. 买卖股票的最佳时机 IV(最多 k 次交易)

LeetCode 188. Best Time to Buy and Sell Stock IV · 难度:困难

1. 核心思路

时,等价于无限次交易(LeetCode 122)。 当 时,维护大小为 的数组:

  • buy[j]:第 次买入后的最大收益;
  • sell[j]:第 次卖出后的最大收益。

转移方程

2. 代码实现

/**
 * @param {number} k
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(k, prices) {
  const len = prices.length;
  if (len <= 1 || k === 0) return 0;
 
  // 退化为无限次交易
  if (k >= Math.floor(len / 2)) {
    let profit = 0;
    for (let i = 1; i < len; i++) {
      if (prices[i] > prices[i - 1]) {
        profit += prices[i] - prices[i - 1];
      }
    }
    return profit;
  }
 
  const buy = new Array(k + 1).fill(-Infinity);
  const sell = new Array(k + 1).fill(0);
  buy[0] = -prices[0];
 
  for (let i = 0; i < len; i++) {
    const p = prices[i];
    buy[0] = Math.max(buy[0], -p);
    for (let j = 1; j <= k; j++) {
      buy[j] = Math.max(buy[j], sell[j - 1] - p);
      sell[j] = Math.max(sell[j], buy[j] + p);
    }
  }
 
  return Math.max(...sell);
};
  • 时间复杂度
  • 空间复杂度