AlgoMaster Logo

Best Time to Buy and Sell Stock II

mediumFrequency9 min readUpdated June 23, 2026

Understanding the Problem

In the single-transaction version of this problem, the goal is to find the one biggest gap between a low price and a later high price. This version removes the limit: you can make as many buy-sell transactions as you want, with one constraint. You can hold at most one share at a time, so you must sell before you buy again.

The problem reduces to picking which days to buy and which to sell so that total profit is maximized. On a price chart, the best strategy is to buy at every valley and sell at every peak. Every upward movement is profit you can capture, and every downward movement is a loss you avoid by not holding.

The approaches below build up to that idea: a brute force over all buy/sell decisions, a two-state dynamic program, and two O(n) solutions that read the profit directly off the price movements.

Key Constraints:

  • 1 <= prices.length <= 3 * 10^4 → With up to 30,000 elements, O(n^2) would give about 9 * 10^8 operations, which is borderline. O(n) or O(n log n) is safe. An exponential brute force (O(2^n)) is far beyond any time limit.
  • 0 <= prices[i] <= 10^4 → Non-negative prices, and the total profit across all transactions fits in a 32-bit integer (worst case: 30,000 days times 10,000 max price difference = 3 * 10^8).

Approach 1: Brute Force (Recursion)

Intuition

Each day offers two choices that depend on the current state. Without a share in hand, we can buy today or skip. With a share in hand, we can sell today or skip.

Recursion can explore every combination of these decisions and return the maximum profit across all paths. Because it tries every legal sequence of trades, it is guaranteed to find the optimal answer.

Algorithm

  1. Define a recursive function that takes the current day index and whether we're currently holding a stock.
  2. Base case: if we've gone past the last day, return 0 (no more profit possible).
  3. If not holding a stock:
    • Option A: Skip this day (recurse to next day, still not holding).
    • Option B: Buy today (recurse to next day, now holding, subtract today's price).
  4. If holding a stock:
    • Option A: Skip this day (recurse to next day, still holding).
    • Option B: Sell today (recurse to next day, not holding, add today's price).
  5. Return the maximum of both options at each step.

Example Walkthrough

1Initialize: day=0, holding=false, profit=0
0
7
day
1
1
2
5
3
3
4
6
5
4
1/7

Code

Many branches reach the same state. solve(day=5, holding=false) returns the same value no matter which trades came before it, yet the recursion recomputes it once per path. There are only n * 2 distinct (day, holding) states, and computing each one once collapses the exponential tree into linear work.

Approach 2: Dynamic Programming

Intuition

The result of solve(day, holding) depends only on the day index and whether we hold a share, so two values per day summarize every possible trading history up to that point.

We can define two DP states:

  • notHolding[i] = maximum profit at the end of day i when we do NOT hold a stock.
  • holding[i] = maximum profit at the end of day i when we DO hold a stock.

The transitions are:

  • notHolding[i] = max(notHolding[i-1], holding[i-1] + prices[i]): either we already weren't holding (skip), or we sell today.
  • holding[i] = max(holding[i-1], notHolding[i-1] - prices[i]): either we were already holding (skip), or we buy today.

Since each day only depends on the previous day, we don't need arrays. Two variables are enough.

Algorithm

  1. Initialize notHolding = 0 (start with no stock, zero profit) and holding = -prices[0] (if we buy on day 0).
  2. For each day from 1 to n-1:
    • Calculate new notHolding = max of (keep not holding) or (sell today).
    • Calculate new holding = max of (keep holding) or (buy today).
  3. Return notHolding (we want to end without holding any stock for maximum profit).

Example Walkthrough

1Initialize: notHolding=0, holding=-7 (bought at price 7)
0
7
day 0
1
1
2
5
3
3
4
6
5
4
1/6

Code

The DP already runs in O(n) time and O(1) space, so the remaining improvements are conceptual rather than asymptotic. With no fees, cooldowns, or transaction limits, the optimal buy and sell days can be read directly off the shape of the price curve, which is what the next two approaches do.

Approach 3: Peak-Valley Scan

Intuition

The chart picture from the introduction translates directly into code: buy at the bottom of every drop (a valley) and sell at the top of the rise that follows (a peak). A single pointer can find these pairs in order. From the current position, walk forward while the price keeps falling to find the next valley, then walk forward while the price keeps rising to find the peak after it. Each valley-to-peak climb is one transaction, and its profit is peak minus valley.

Beyond the profit total, this scan recovers the actual buy and sell days, and it uses the fewest transactions that still achieve the maximum profit, since each maximal climb is captured in a single trade.

Algorithm

  1. Initialize i = 0 and profit = 0.
  2. While a next day exists (i + 1 < n):
    • Advance i while prices[i] >= prices[i + 1] to find the next valley, and record valley = prices[i].
    • Advance i while prices[i] <= prices[i + 1] to find the peak that follows, and record peak = prices[i].
    • Add peak - valley to profit.
  3. Return profit.

The >= and <= comparisons handle plateaus: equal prices are skipped during the descent and walked through during the climb. If prices end on a decline, the final valley is the last day, the peak equals the valley, and that pair adds nothing.

Example Walkthrough

1Initialize: i=0, profit=0
0
7
i
1
1
2
5
3
3
4
6
5
4
1/6

Code

The peak-valley scan still tracks where each transaction starts and ends. If only the total profit matters, that bookkeeping can be dropped entirely.

Approach 4: Greedy (Sum of Positive Differences)

Intuition

A multi-day hold decomposes into consecutive daily differences. With prices 1, 2, 3, 5 over four days, buying at 1 and selling at 5 earns 5 - 1 = 4, and that same profit telescopes into (2 - 1) + (3 - 2) + (5 - 3). Every transaction's profit, no matter how long the hold, is a sum of one-day differences.

So no transaction structure is needed at all. If prices[i] > prices[i - 1], that difference is profit captured by holding from day i - 1 to day i. If the price drops, don't hold through the drop. Summing every positive consecutive difference gives the answer.

This sum is optimal for two reasons. It is an upper bound: any transaction's profit telescopes into the daily differences inside its span, which is at most the sum of the positive ones, and transactions never overlap, so no difference is counted twice. It is also achievable: holding across exactly the rising days is a legal strategy because transactions are unlimited and fee-free. The total matches what the peak-valley scan computes, accumulated one day at a time instead of one climb at a time.

Algorithm

  1. Initialize profit = 0.
  2. For each day from 1 to n-1:
    • If today's price is higher than yesterday's, add the difference to profit.
  3. Return profit.

Example Walkthrough

1Initialize: profit = 0
0
i-1
7
1
1
i
2
5
3
3
4
6
5
4
1/6

Code