LeetCode 123:买卖股票的最佳时机 III

LeetCode 123:买卖股票的最佳时机 III

问题核心与思路拆解

需计算最多两次交易 的最大利润,核心是通过 四个状态 建模交易过程:

  • buy1:仅完成第一次买入(未卖出);
  • sell1:完成第一次买卖(一笔交易);
  • buy2:完成一笔交易后,第二次买入(未卖出);
  • sell2:完成两次买卖(两笔交易)。

通过状态转移方程 逐天更新这四个状态,最终 sell2 即为最大利润(覆盖"0、1、2次交易"的最优解)。

算法步骤详解

1. 状态定义
状态 含义 初始值(第0天)
buy1 第一次买入后未卖出 -prices[0](买入第0天股票)
sell1 第一次卖出(完成一笔交易) 0(第0天买入并卖出,利润0)
buy2 完成一笔交易后,第二次买入未卖出 -prices[0](第0天完成一次交易后再买入)
sell2 完成两次卖出(完成两笔交易) 0(第0天完成两次交易,利润0)
2. 状态转移方程

遍历每天价格 price,更新四个状态(依赖前一天的旧状态):

  • buy1 :可保持现状,或在"未交易"状态下买入当前股票:
    buy1=max⁡(旧buy1, −price) \text{buy1} = \max(\text{旧buy1},\ -price) buy1=max(旧buy1, −price)
  • sell1 :可保持现状,或在"第一次买入"状态下卖出当前股票:
    sell1=max⁡(旧sell1, 旧buy1+price) \text{sell1} = \max(\text{旧sell1},\ \text{旧buy1} + price) sell1=max(旧sell1, 旧buy1+price)
  • buy2 :可保持现状,或在"第一次卖出"状态下买入当前股票:
    buy2=max⁡(旧buy2, 旧sell1−price) \text{buy2} = \max(\text{旧buy2},\ \text{旧sell1} - price) buy2=max(旧buy2, 旧sell1−price)
  • sell2 :可保持现状,或在"第二次买入"状态下卖出当前股票:
    sell2=max⁡(旧sell2, 旧buy2+price) \text{sell2} = \max(\text{旧sell2},\ \text{旧buy2} + price) sell2=max(旧sell2, 旧buy2+price)
3. 边界与遍历
  • 特殊情况 :若数组长度 < 2,直接返回 0(无法交易)。
  • 遍历过程 :从第1天开始,逐天更新四个状态,确保每次转移使用前一天的旧值(避免状态污染)。

完整代码(Java)

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        // 特殊情况:无法交易
        if (prices == null || prices.length < 2) {
            return 0;
        }
        
        int n = prices.length;
        // 初始化四个状态(第0天)
        int buy1 = -prices[0];   // 第一次买入
        int sell1 = 0;          // 第一次卖出(买入后立即卖出,利润0)
        int buy2 = -prices[0];   // 第二次买入(第一次卖出后立即买入)
        int sell2 = 0;          // 第二次卖出(两次交易都在第0天,利润0)
        
        // 逐天更新状态(从第1天开始)
        for (int i = 1; i < n; i++) {
            int price = prices[i];
            // 计算新状态(基于前一天的旧状态)
            int newBuy1 = Math.max(buy1, -price);          // 保持或新买入(未交易→买入)
            int newSell1 = Math.max(sell1, buy1 + price);  // 保持或卖出(第一次买入→卖出)
            int newBuy2 = Math.max(buy2, sell1 - price);   // 保持或新买入(第一次卖出→买入)
            int newSell2 = Math.max(sell2, buy2 + price);  // 保持或卖出(第二次买入→卖出)
            
            // 更新状态(覆盖旧值,供下一轮使用)
            buy1 = newBuy1;
            sell1 = newSell1;
            buy2 = newBuy2;
            sell2 = newSell2;
        }
        
        // sell2 覆盖了0、1、2次交易的最优解
        return sell2;
    }
}

代码逻辑验证(示例1:prices = [3,3,5,0,0,3,1,4]

天数 i 价格 price buy1 变化 sell1 变化 buy2 变化 sell2 变化
0 3 -3 0 -3 0
1 3 max(-3, -3)=-3 max(0, -3+3=0)=0 max(-3, 0-3=-3)=-3 max(0, -3+3=0)=0
2 5 max(-3, -5)=-3 max(0, -3+5=2)=2 max(-3, 2-5=-3)=-3 max(0, -3+5=2)=2
3 0 max(-3, -0=0)=0 max(2, 0+0=0)=2 max(-3, 2-0=2)=2 max(2, 2+0=2)=2
4 0 max(0, -0=0)=0 max(2, 0+0=0)=2 max(2, 2-0=2)=2 max(2, 2+0=2)=2
5 3 max(0, -3)=0 max(2, 0+3=3)=3 max(2, 3-3=0)=2 max(2, 2+3=5)=5
6 1 max(0, -1)=0 max(3, 0+1=1)=3 max(2, 3-1=2)=2 max(5, 2+1=3)=5
7 4 max(0, -4)=0 max(3, 0+4=4)=4 max(2, 4-4=0)=2 max(5, 2+4=6)=6

最终 sell2=6,与示例1结果一致。

复杂度分析

  • 时间复杂度O(n)(遍历数组一次,每次更新4个状态,O(1) 操作)。
  • 空间复杂度O(1)(仅用4个变量存储状态,无额外空间)。

该方法通过 状态压缩+动态规划 ,精准建模"两次交易"的所有可能状态,确保每一步转移高效且无冗余。核心是对"买入/卖出"操作的状态拆分,以及利用滚动更新避免空间浪费,是交易类动态规划问题的经典解法。

相关推荐
大阳1232 分钟前
数据结构(概念及链表)
c语言·开发语言·数据结构·经验分享·笔记·算法·链表
2501_9247319931 分钟前
驾驶场景玩手机识别:陌讯行为特征融合算法误检率↓76% 实战解析
开发语言·人工智能·算法·目标检测·智能手机
爱编程的鱼1 小时前
计算机(电脑)是什么?零基础硬件软件详解
java·开发语言·算法·c#·电脑·集合
洛生&2 小时前
【abc417】E - A Path in A Dictionary
算法
亮亮爱刷题2 小时前
算法提升之数学(快速幂+逆元求法)
算法
恣艺2 小时前
LeetCode 124:二叉树中的最大路径和
算法·leetcode·职场和发展
1白天的黑夜12 小时前
前缀和-1314.矩阵区域和-力扣(LeetCode)
c++·leetcode·前缀和
weisian1512 小时前
力扣经典算法篇-42-矩阵置零(辅助数组标记法,使用两个标记变量)
算法·leetcode·矩阵
geoyster3 小时前
20250802-102508010-CP
算法