AlgoMaster Logo

Best Time to Buy and Sell Stock III

Last Updated: April 2, 2026

hard
5 min read

Understanding the Problem

This is the third problem in the "Best Time to Buy and Sell Stock" series, and it introduces a specific constraint: at most two transactions. In Stock I, you could make one transaction. In Stock II, you had unlimited transactions. Here, you are capped at two.

Why does limiting it to two make things harder? Because now you need to decide where to "split" your effort. Should you use both transactions, or is one enough? If you use both, how do you pick two non-overlapping buy-sell windows that maximize the combined profit? You cannot just find the single best transaction and then find the second best in the remaining days, because the two transactions might interact. For example, one large profit might span a range where two smaller but better-combined profits exist.

The key observation is that any two non-overlapping transactions divide the array into a "left transaction" (days 0 to some split point) and a "right transaction" (from that split point onward). This partitioning idea, combined with the fact that we can precompute the best single transaction from the left and right, leads to an elegant O(n) solution.

Key Constraints:

  • 1 <= prices.length <= 10^5 --> With n up to 100,000, we need O(n log n) or better. An O(n^2) approach would hit 10^10 operations, which is far too slow.
  • 0 <= prices[i] <= 10^5 --> Prices are non-negative. The maximum possible profit from a single transaction is 10^5, so two transactions can yield at most 2 * 10^5, fitting comfortably in a 32-bit integer.

Approach 1: Brute Force (Try All Split Points)

Intuition

The most straightforward way to think about two transactions is: any valid pair of transactions can be separated by a split point. The first transaction happens entirely within days 0 to i, and the second happens entirely within days i to n-1 (they can share the boundary day since selling and buying on the same day is a no-op).

So if we could quickly answer "what is the best single transaction in days 0..i?" and "what is the best single transaction in days i..n-1?", we could try every split point and pick the maximum sum. Finding the best single transaction in a subarray is the classic Stock I problem: track the minimum price seen so far and the maximum profit. But doing this from scratch for every split point would be O(n) per split, giving O(n^2) total.

Algorithm

  1. For each possible split point i from 0 to n-1:
    • Compute the best single transaction profit in days 0..i by scanning left to right, tracking the minimum price.
    • Compute the best single transaction profit in days i..n-1 by scanning right to left, tracking the maximum price.
    • Record the sum of these two profits.
  2. Return the maximum sum across all split points.

Example Walkthrough

1Split at i=0: left=[3] profit=0, right=[3,3,5,0,0,3,1,4] profit=4, total=4
0
3
split
1
3
2
5
3
0
4
0
5
3
6
1
7
4
right: profit=4
1/5

Code

For each split point, we recompute the best single transaction on each side from scratch. But the left subarray only grows by one element as we move the split point. What if we precomputed the best left and right profits so that evaluating each split becomes O(1)?

Approach 2: Precomputed Left and Right Profits

Intuition

The brute force recomputes the best single transaction for every split point. But notice that "best single transaction in days 0..i" is just the Stock I problem solved incrementally. As we scan left to right, we track the minimum price so far and the best profit so far. We can store the answer for every prefix in an array leftProfit[i] in a single O(n) pass.

Similarly, the "best single transaction in days i..n-1" can be computed by scanning right to left, tracking the maximum price so far, stored in rightProfit[i].

Once we have both arrays, the answer is simply max(leftProfit[i] + rightProfit[i]) for all i.

Algorithm

  1. Create an array leftProfit of size n. Scan left to right, tracking the minimum price seen so far. At each day i, leftProfit[i] = max profit from a single transaction in days 0..i.
  2. Create an array rightProfit of size n. Scan right to left, tracking the maximum price seen so far. At each day i, rightProfit[i] = max profit from a single transaction in days i..n-1.
  3. Iterate over all split points i from 0 to n-1, computing leftProfit[i] + rightProfit[i]. Return the maximum.

Example Walkthrough

1Left pass start: min=3, leftProfit[0]=0
0
0
i
1
0
2
0
3
0
4
0
5
0
6
0
7
0
1/5

Code

The O(n) time is already optimal, but we are using O(n) extra space. Can we track all possible stages of the two transactions simultaneously using only a constant number of variables?

Approach 3: State Machine (Optimal)

Intuition

Instead of splitting the array, think about the states you can be in as you process each day: before any transaction, after first buy, after first sell, after second buy, after second sell. At each day, you can either stay in your current state or move to the next one. We track the best profit achievable in each state using four variables.

The elegant part: buy1 tracks the minimum cost of the first buy. sell1 tracks the best profit after completing the first transaction. buy2 builds on sell1, effectively using the first transaction's profit as a "discount" for the second buy. And sell2 tracks the final answer.

Algorithm

  1. Initialize buy1 = -infinity, sell1 = 0, buy2 = -infinity, sell2 = 0.
  2. For each price in the array:
    • buy1 = max(buy1, -price): Buy at the lowest price so far.
    • sell1 = max(sell1, buy1 + price): Sell the first stock for maximum profit.
    • buy2 = max(buy2, sell1 - price): Buy the second stock, using first transaction's profit.
    • sell2 = max(sell2, buy2 + price): Sell the second stock for maximum total profit.
  3. Return sell2.

Example Walkthrough

1Init: buy1=-inf, sell1=0, buy2=-inf, sell2=0
0
3
1
3
2
5
3
0
4
0
5
3
6
1
7
4
1/7

Code