Finding the best time to trade a stock is a practical example of optimizing decisions over time using simple scanning logic. Even with just one buy and one sell allowed, choosing the right pair of days can be surprisingly tricky because prices fluctuate and the best opportunity isn’t always obvious at first glance. This problem highlights how tracking key information as you move through the list can replace slow, repetitive comparisons with an efficient one-pass strategy.
Problem statement
You’re given a list of daily stock prices, where prices[i] is the price on day i. Your task is to pick one day to buy and a later day to sell to achieve the maximum possible profit.
Constraints:
- 1 ≤
prices.length≤ $10^5$ - 0 ≤
prices[i]≤ $10^4$
Examples
| Input | Output | Explanation |
|---|---|---|
| prices = [8, 1, 2, 10] | 9 | Buy at 1, sell at 10, profit = 10 – 1 = 9. |
| prices = [2, 10, 1, 3] | 8 | Best is buy at 2, sell at 10, profit = 10 – 2 = 8 (better than 1 -> 3). |
| prices = [9, 3, 5, 7, 16, 6] | 13 | Buy at 3, sell at 16, profit = 16 – 3 = 13. |
| prices = [4, 2, 6, 1, 7] | 6 | Buy at 1, sell at 7, profit = 7 – 1 = 6. |
| prices = [8, 7, 4, 2, 1] | 0 | Prices keep decreasing, so there is no profitable transaction. Return 0. |
Why brute force is impractical
A simple approach is to first try every valid buy–sell combination by picking a day i as the buying day, and for that day, try every later day j > i as the selling day. Then we compute the profit as prices[j] - prices[i] and track the maximum profit from all buy-sell combinations.
While this logic is easy to reason about, it quickly becomes impractical. For an array of n days:
- Day 0 compares with n – 1 future days.
- Day 1 compares with n – 2 future days.
- …
- Day n – 2 compares with just one future day.
In total, this results in roughly n(n−1)/2 comparisons.
With up to 105 days, that means billions of profit calculations, most of which repeat similar checks over overlapping ranges of days. This not only makes the solution too slow to run within time limits, but also wastes effort re-evaluating the same price differences again and again.
Optimized approach using Greedy Techniques
The greedy optimization comes from the insight that for any day you choose to sell, the best possible buy must be the cheapest (lowest) price that occurred before that day. So, as you scan from left to right, you only need to track:
- The minimum price so far, i.e., the cheapest buying opportunity seen up to today
- The best profit so far, i.e., the maximum profit achievable up to today
For each day, treat the current price as a potential selling price:
- Compute the profit if you sell today as current price − minimum price so far
- Update best profit if the current profit is larger
- Update minimum price so far if today’s price is lower
Solution steps
- Initialize
min_priceto the first day’s price. - Initialize
best_profitto0. - For each subsequent price
pin the input array:- Compute the profit if selling today:
p - min_price. - Update
best_profitif this current profit is larger. - Update
min_priceifpis smaller than the currentmin_price.
- Compute the profit if selling today:
- Return
best_profit.
Take a look at the illustration below to understand the solution more clearly.
Python Implementation
Let’s look at the code for the solution we just discussed.
Time complexity
The solution runs in linear time because it scans the prices array once, doing constant work per day. If n is the number of days, the runtime grows proportionally with n.
Space complexity
The solution uses constant extra space. It only stores a few variables, regardless of how large prices is.
Edge cases
- Single day (no possible sell day)
Example:prices= [5]
The loop doesn’t run, andbest_profitstays 0, which is correct. - Always decreasing prices
Example:prices= [7, 6, 4, 3, 1]
Every computed profit is <= 0, sobest_profitremains 0. - Profit is made by buying at a later low point and then selling after the price rises.
Example:prices= [5, 4, 3, 2, 10]min_pricekeeps updating until 2, then profit becomes 10 – 2 = 8. - Multiple equal minima
Example:prices= [3, 1, 1, 2]min_pricebecomes 1 and stays there, allowing the correct profit 2 – 1 = 1.
Common pitfalls
- Trying to “buy and sell on the same day”
The problem requires the sell day to be in the future. A correct scan only considers profits using a minimum from earlier days. - Updating
min_pricebefore computing today’s profit
If you updatemin_pricefirst, you might accidentally allow buying today and selling today, which can hide bugs in some implementations. - Returning negative profit
The best answer must be0if no profit is possible. Initializebest_profitto 0, not to a negative number. - Overcomplicating with multiple transactions logic
This problem is strictly one transaction. Strategies that track multiple buy/sell segments are solving a different problem.