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个变量存储状态,无额外空间)。

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

相关推荐
hello_lain2 小时前
9.1 简单排序(冒泡、插入)(排序(上))
c语言·数据结构·算法·排序算法
炬火初现2 小时前
Hot100——普通数组
算法·leetcode·职场和发展
啊我不会诶2 小时前
【数据结构】字典树
数据结构·算法
信奥卷王2 小时前
[GESP202403 五级] 成绩排序
数据结构·算法
小欣加油3 小时前
leetcode 494 目标和
c++·算法·leetcode·职场和发展·深度优先
Jiezcode4 小时前
LeetCode 55.跳跃游戏
c++·算法·leetcode·游戏
wheeldown5 小时前
【Leetcode高效算法】用双指针策略打破有效三角形的个数
python·算法·leetcode
蒙奇D索大5 小时前
【数据结构】考研重点掌握:顺序查找算法实现与ASL计算详解
数据结构·笔记·学习·考研·算法·改行学it
TTGGGFF5 小时前
MATLAB仿真:编程基础实验全解析——从入门到实战
数据结构·算法·matlab
Ivanqhz5 小时前
LR算法中反向最右推导(Reverse RightMost Derivation)
人工智能·算法