Best Time to Buy and Sell Stock III raises the stakes: instead of one trade, you may do at most two. The clever part is that you do not need a 2-D table — four running variables sweep the prices once and give the answer.
Problem. You are given an array prices where prices[i] is the price of a stock on day i. You
may complete at most two transactions (buy then sell, and the second buy must come after the first
sell — no holding two positions at once). Return the maximum profit.
Example: prices = [3, 3, 5, 0, 0, 3, 1, 4] → answer 6 (buy at 0 sell at 3, then buy at 1 sell at 4: 3 + 3 = 6).
The slow way first
The brute force is to pick a day to split the timeline, run the single-transaction solution on the left half and on the right half, and add the two best profits. Trying every split point is O(n²) (and naive pair-checking inside each half is even worse). For a long price history that is far too slow.
The question to ask: can I carry the best result of each stage forward as I walk the array, instead of recomputing? Yes — and it collapses to four numbers.
The idea: four running balances
Think of your balance (cash on hand) after each action, where buying subtracts the price and selling adds it. Track the best possible balance after each of the four stages:
buy1— best balance after the first buy (we spentp, so it is-por better).sell1— best balance after the first sell (buy1 + p).buy2— best balance after the second buy, paid for out of the first profit (sell1 - p).sell2— best balance after the second sell (buy2 + p). This is the answer.
Each day, we update all four with the current price p. Because buy2 is built from sell1, and sell2 from buy2, the second transaction automatically reuses the profit of the first. Updating them top-to-bottom on the same price is fine: it just means "I could also buy and sell again today," which never hurts.
Walk through it
Step through the animation. The pointer p scans the prices left to right; the four labels show each best balance updating. Watch sell2 climb: it only ever goes up, and by the last day it holds the maximum two-transaction profit, 6.
Pseudocode
buy1, buy2 = -infinity, -infinity # best balance after each buy
sell1, sell2 = 0, 0 # best balance after each sell
for each price p in prices:
buy1 = max(buy1, -p) # spend p on the first buy
sell1 = max(sell1, buy1 + p) # recover p on the first sell
buy2 = max(buy2, sell1 - p) # spend p again, funded by sell1
sell2 = max(sell2, buy2 + p) # recover p on the second sell
return sell2 # most money after <= 2 tradesThe Python solution
def max_profit(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 sell2buy1/buy2start at negative infinity so the very first price forces a real buy.sell1/sell2start at0: doing no trade yields zero profit.- Line 5:
buy1is the best balance if we are currently holding the first stock — paypnow, or keep an earlier (cheaper) buy. - Line 6:
sell1addspto the best first buy — the best profit from one completed trade. - Line 7:
buy2spendspagain but starts fromsell1, so the second purchase is funded by the first profit. - Line 8:
sell2is the final answer — the best balance after both trades complete.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (split point) | O(n²) (slow) | try every divider, solve each half |
| Four variables (this solution) | O(n) (moderate) | one pass, constant work per day |
O(1) (fast)We replaced a 2-D DP table with four scalars, so the space is O(1). The same trick generalizes: for k transactions you keep 2k running balances.
When this pattern shows up
When a DP has a small fixed number of "stages" (here: buy, sell, buy, sell), you can often drop the table and carry one variable per stage. Update them in stage order each step and read the last one as the answer. This is the rolling-variable trick that turns O(n) space into O(1).
Update the four lines in order (buy1, sell1, buy2, sell2) each day. The chain sell1 then buy2
then sell2 is what links the two transactions; reordering or computing them independently breaks the
dependency and gives wrong profits.
Practice
For prices = [3, 3, 5, 0, 0, 3, 1, 4], what two trades produce the answer of 6?
1. What does sell2 represent at the end of the loop?
2. Why are buy1 and buy2 initialized to negative infinity?
3. How does the second transaction reuse the first profit?
4. What is the space complexity of this solution?