Skip to content

Coding patternsBlind 75

Best Time to Buy and Sell Stock

The best sale on any day buys at the lowest price seen before it, so one pass that remembers the lowest price finds the answer. Solved two ways, brute force in O(n²) and one pass in O(n), with a step-through, code in five languages and the Big O worked out from the code.

You are given an array prices of numbers (call its length n). prices[i] is the price of one share on day i.

You may buy one share on one day and sell it on a later day. Return the largest profit you can make. If no trade makes money, return 0, because you can simply not trade.

Example 1

Input: prices = [6, 2, 7, 1, 5, 3]

Output: 5

Buy on day 1 at 2 and sell on day 2 at 7. The profit is 7 − 2 = 5. Buying at 1 later only reaches 5 − 1 = 4.

price by day6021721354357 − 2 = 5

Example 2

Input: prices = [9, 7, 4, 1]

Output: 0

The price only falls. Every trade loses money, so the best choice is not to trade.

Example 3

Input: prices = [3, 8]

Output: 5

Buy on day 0 and sell on day 1.

  • 1 ≤ n ≤ 100,000
  • 0 ≤ prices[i] ≤ 10,000
How many trades are allowed?
One buy and one sell. Not several.
Can I sell before I buy?
No. The sell day must come after the buy day.
What if every trade loses money?
Return 0. Not trading is always allowed.
Can I buy and sell on the same day?
That makes 0 profit, which is the same as not trading. It never changes the answer.

For a fixed sell day, the best buy day is simply the cheapest day before it. So as we walk the days in order, one number, the lowest price so far, tells us the best trade ending today. It is like a window whose left edge jumps to each new low and whose right edge is today.

This problem has two approaches. There is no useful step between them.

One pass O(n)

Pick any sell day. The best day to have bought is the cheapest day before it. So walk the days in order and remember just one number: low, the lowest price so far.

On each day, first ask: if I sell today, what is the profit? That is price − low. Keep the best. Then, if today’s price is a new low, update low, so later days can buy here.

Why it never misses the answer: the best trade sells on some day j. When the loop reaches j, low is the cheapest price before j, so the profit it checks is the best possible for that day.

price by day602172135435▲ low
low
6
best
0
  1. low = prices[0]
  2. best = 0
  3. for i in range(1, len(prices)):
  4. best = max(best, prices[i] - low)
  5. low = min(low, prices[i])
  6. return best

Day 0: the lowest price so far is 6. Nothing has been sold yet, so best = 0.

1 / 9
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// One pass: the best sale today buys at the lowest price seen before today.
// One loop, so O(n) time. Two numbers, so O(1) memory.
public int maxProfit(int[] prices) {
int low = prices[0];
// 0 means "do not trade", which is always allowed.
int best = 0;
for (int i = 1; i < prices.length; i++) {
// Sell today: the cheapest earlier day is the best day to have bought.
best = Math.max(best, prices[i] - low);
// Update low after selling, so we never buy and sell on the same day.
low = Math.min(low, prices[i]);
}
return best;
}
}
Time O(n)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    Each run of the loop does one subtract, one max and one min. That is a fixed amount of work, so each run is 1 step. There is one loop.

    1. low = prices[0]
    2. best = 0
    3. for i in range(1, len(prices)):
    4. best = max(best, prices[i] - low)
    5. low = min(low, prices[i])
    6. return best
  2. 2 Count the runs on Example 1

    Example 1 has 6 days. The loop starts on day 1, because day 0 has nothing before it to buy from. So it runs for days 1 to 5. The table shows each run.

    5 runs.

    day12345
    price27153
    low before62211
    profit-45-142
  3. 3 Write the count for any n

    n − 1 times

    Check it with Example 1: 6 − 1 = 5.

  4. 4 Keep only the part that grows fastest

    Drop the − 1. One run more or less does not change how the work grows. What is left is n.

    So the time is O(n).

  5. S Space: count the extra memory the same way

    The code keeps low, best and i. That is 3 numbers for any n, so the space is O(1).

    lowbesti

2,000 random price lists of 1 to 10 days, prices 0 to 8, so flat days, falling runs and single days are common. The brute force and the one pass gave the same answer every time.

Largest inputsResult
100,000 prices (the most allowed) in a V shape: down from 10,000 to 0, then back up. The best day to buy is in the middle. One pass.0.01 s, and under 0.001 MB of extra memory: just two variables.
2,000 prices, a smaller V. Brute force, about 2 million pairs.0.09 s. At 100,000 prices it would check 5 billion pairs, which is why the one pass matters.