题目描述
思路解析
一句话答案:LeetCode 123 买卖股票的最佳时机 III:最多两笔交易,用 buy1、sell1、buy2、sell2 四个状态串成一条链、每天各更一遍,答案取 sell2。时间 O(n)、空间 O(1)。
买卖股票 III 比前两版多了哪条硬限制
给数组 prices,prices[i] 是第 i 天股价,最多完成两笔交易、任何时刻最多持一股,求最大总利润。它比只买卖一次的 121、无限次的 122 多了「最多两笔」这条卡子。题面 prices=[3,3,5,0,0,3,1,4,2] 答案 6:0 买、3 卖赚 3,再 1 买、4 卖赚 3。
为什么把两笔的买卖点全枚举会算爆
要挑出满足 买1<卖1<买2<卖2 的四个时点,全枚举是 O(n⁴)(大 O 记号,描述规模变大时操作数怎么涨)级别,n 稍大就算不完,还有大量重复子问题。把算过的结果存下来复用,就是动态规划(把『到今天为止、四个里程碑各自能达到的最好利润』记下来直接用)。
buy1、sell1、buy2、sell2 这四个状态各盯着什么
不用枚举哪天买卖,只盯四个里程碑此刻能拿到的最好利润。buy1 是第一次买入后的账面利润,买要掏钱所以是负数(所以 buy1 越接近 0、买得越便宜越好,别被负号唬住),就是到今天最低价取负。sell1 是在 buy1 上今天按价 p 卖出后的最大利润。buy2 是拿第一次卖完的钱再买后的最大利润。sell2 是第二次卖出后的最大利润,就是答案。四态一环扣一环,每个都建在前一个已算好的最优上顺推。
四条转移怎么写,为什么同一轮能用刚算好的前一个
每个状态每天都在两条路里取大:保持昨天的值,或今天就把这一步做掉。写成式子是 buy1=max(buy1,-p)、sell1=max(sell1,buy1+p)、buy2=max(buy2,sell1-p)、sell2=max(sell2,buy2+p),依次是买一、卖一、再买二、卖二。
更新次序不能乱:同一天先更 buy1,sell1 就用刚更的 buy1,buy2 用刚更的 sell1,sell2 用刚更的 buy2。每个状态只依赖前一个的当前值,整张表压成四个变量滚着用,这叫滚动(只留当前值、就地覆盖),空间 O(1)。
拿题面这九天的股价亲手滚一遍四态
prices=[3,3,5,0,0,3,1,4,2],初值 buy1=buy2=负无穷、sell1=sell2=0。第 0 天价 3:buy1=-3、buy2=-3(buy2 此刻只是占位、还没真第二次买)、sell1=sell2=0。第 2 天价 5:sell1=max(0,-3+5)=2、sell2=2。第 3 天价 0:buy1=0、buy2=max(-3,2-0)=2。第 5 天价 3:sell1=3、sell2=max(2,2+3)=5。第 7 天价 4:sell1=4、sell2=max(5,2+4)=6。其余各天四态不变。最后 sell2=6,正是题面答案。
buy 初值写成 0,利润为什么会凭空虚高
扫一遍 prices、每天四次取大,时间 O(n);只用四个变量滚动,空间 O(1)。buy1、buy2 初值写成 0 是最常见的错,那等于没花钱就凭空持股、利润偏高,得设成负无穷;sell1、sell2 才初值 0。顺序也不能乱,sell1 挪到 buy1 前面就会读到没更新的旧 buy1,链当场断。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
思路一句话:拆成四个状态 买1→卖1→买2→卖2,一环扣一环,每天各更新一遍。下面一步步演给你看。
顶行是每天股价(固定)。下面四行 buy1 / sell1 / buy2 / sell2,待逐天填——它们就是两笔交易的四个里程碑。
第 0 天开局:第一次买入花掉 3 元 → buy1=-3;当天买当天卖利润为 0 → sell1=sell2=0;第二次买入又垫一笔 → buy2=-3。
落子四态:buy1=-3,sell1=0,buy2=-3,sell2=0。
第 1 天(价 3):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=-3(保持),sell1=0(保持),buy2=-3(保持),sell2=0(保持)。
第 2 天(价 5):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=-3(保持),sell1=2(今天第一次卖出更划算),buy2=-3(保持),sell2=2(今天第二次卖出更划算)。
第 3 天(价 0):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(今天第一次买入更划算),sell1=2(保持),buy2=2(今天第二次买入更划算),sell2=2(保持)。
第 4 天(价 0):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(保持),sell1=2(保持),buy2=2(保持),sell2=2(保持)。
第 5 天(价 3):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(保持),sell1=3(今天第一次卖出更划算),buy2=2(保持),sell2=5(今天第二次卖出更划算)。
第 6 天(价 1):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(保持),sell1=3(保持),buy2=2(保持),sell2=5(保持)。
第 7 天(价 4):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(保持),sell1=4(今天第一次卖出更划算),buy2=2(保持),sell2=6(今天第二次卖出更划算)。
第 8 天(价 2):四态各看两条路——保持昨天 或 今天做这一步动作。注意 sell1 依赖刚算出的 buy1、buy2 依赖刚算出的 sell1,串成一条链。
取较大的:buy1=0(保持),sell1=4(保持),buy2=2(保持),sell2=6(保持)。
最右的 sell2[8]=6 就是最大利润——两笔交易全部落袋,手里不留股。
边界先想清:能不能凑满两笔不强求,sell2 自动取最优。
两个高频追问。
参考代码
def maxProfit(prices): buy1 = buy2 = float("-inf") sell1 = sell2 = 0 for p in prices: buy1 = max(buy1, -p) sell1 = max(sell1, buy1 + p) buy2 = max(buy2, sell1 - p) sell2 = max(sell2, buy2 + p) return sell2复杂度
- 时间:O(n),一遍线性递推
- 空间:O(1),四个状态变量滚动
易错点
面试追问把动画讲成自己的话
追问改成「最多 k 笔」怎么办?
追问为什么答案是 sell2 而不是 sell1?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
分割回文串 II
LeetCode 132 · 困难 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题