Best Time to Buy and Sell Stock: Optimal Solution (Data-Backed Guide)

By DSA Prep Team · March 4, 2026 · 9 min read · Algorithms

LeetCode #121 is the second most frequently asked problem in coding interviews globally — and it's not even close to being "just an easy problem."

Our analysis of 10,385 verified interview questions across 259 companies shows Best Time to Buy and Sell Stock appeared 199 times across 63 companies — including Amazon, Google, Apple, Bloomberg, Goldman Sachs, and BlackRock.

The catch: most candidates solve it with a brute force O(n²) approach and leave marks on the table. The optimal solution is a single pass, O(n) greedy scan that takes under 10 lines of code. This guide walks you through every approach, every variation, and exactly how to explain it in an interview.

Table of Contents

The Data: Why This Problem Gets Asked Everywhere

Best Time to Buy and Sell Stock is not just a LeetCode warm-up. It's an interview staple across every tier of company — from FAANG giants to quant hedge funds to unicorn startups.

MetricValue
Total Interview Appearances199 verified mentions
Companies That Ask It63 companies
Global Rank#2 out of 10,385 questions
DifficultyEasy (follow-ups go to Hard)
Core PatternsArray, Greedy, Dynamic Programming

Why is this problem so universally popular? Because it tests a fundamental interviewing skill: can you convert a naive O(n²) brute force into a clean O(n) one-pass scan? This "track the minimum so far" technique appears across dozens of harder problems. Mastering it here unlocks a whole family of solutions.

📌 Top Companies That Ask This Problem

Amazon, Google, Apple, Bloomberg, Goldman Sachs, BlackRock, Adobe, Atlassian, Airbnb, Microsoft, BNY Mellon, Bank of America, American Express, Agoda, Accenture, and 48 more.

Problem Statement

LeetCode #121 — Best Time to Buy and Sell Stock

You are given an array prices where prices[i] is the price of a given stock on the i-th day. You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock. Return the maximum profit you can achieve. If you cannot achieve any profit, return 0.

Examples
Input:  prices = [7, 1, 5, 3, 6, 4]
Output: 5
Explanation: Buy on day 2 (price=1), sell on day 5 (price=6). Profit = 6 - 1 = 5.

Input:  prices = [7, 6, 4, 3, 1]
Output: 0
Explanation: Prices only decrease. No profitable transaction possible.

Input:  prices = [2, 4, 1]
Output: 2
Explanation: Buy on day 1 (price=2), sell on day 2 (price=4). Profit = 4 - 2 = 2.

⚠️ Key Constraint

You must buy before you sell. You cannot buy on day 5 and sell on day 2. This makes it fundamentally different from "find max - find min" in any order.

Approach 1: Brute Force O(n²)

The naive instinct is to check every possible buy-sell pair and track the maximum profit.

Python — Brute Force O(n²)
def maxProfit(prices):
    max_profit = 0

    for i in range(len(prices)):
        for j in range(i + 1, len(prices)):
            profit = prices[j] - prices[i]
            max_profit = max(max_profit, profit)

    return max_profit
MetricBrute ForceOptimal
Time ComplexityO(n²)O(n)
Space ComplexityO(1)O(1)
Passes Interview?❌ Not if you stop here✅ Yes

What to say in the interview: "A brute force approach would be to try every buy-sell pair — O(n²) time. But we don't need to revisit old prices if we track the minimum price seen so far. That gives us O(n) time in a single pass."

Approach 2: Optimal One-Pass Greedy O(n)

The key insight: as you scan left to right, you want to buy at the lowest price seen so far and sell at the highest price after that buy point.

You don't need to look back. At every new price, you ask two questions:

  1. Is this price lower than my current minimum? If yes — update min_price. This is now my best buy point.
  2. Is current_price - min_price greater than my current best profit? If yes — update max_profit.

That's it. One pass, two variables.

Visual Walkthrough

Tracing prices = [7, 1, 5, 3, 6, 4]:

DayPricemin_priceprofit todaymax_profit
07700
111 (updated)00
25144 (updated)
33124
46155 (updated)
54135

Final answer: 5. Buy at price 1 (day 1), sell at price 6 (day 4).

Code in Python, Java & JavaScript

Python 3
def maxProfit(prices: list[int]) -> int:
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        if price < min_price:
            min_price = price           # found a cheaper buy point
        elif price - min_price > max_profit:
            max_profit = price - min_price  # found a better profit

    return max_profit
Java
class Solution {
    public int maxProfit(int[] prices) {
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;

        for (int price : prices) {
            if (price < minPrice) {
                minPrice = price;
            } else if (price - minPrice > maxProfit) {
                maxProfit = price - minPrice;
            }
        }

        return maxProfit;
    }
}
JavaScript
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let minPrice = Infinity;
    let maxProfit = 0;

    for (const price of prices) {
        if (price < minPrice) {
            minPrice = price;
        } else if (price - minPrice > maxProfit) {
            maxProfit = price - minPrice;
        }
    }

    return maxProfit;
};

Complexity Analysis

📊 Complexity Summary

This is the best achievable complexity for this problem. You must examine every price at least once, so O(n) time is optimal. The O(1) space is a bonus — this solution is strictly better than brute force in time with no space penalty.

Interview-ready explanation: "Time is O(n) because we do a single pass over the array with constant work at each step. Space is O(1) because we only maintain two scalar variables — no auxiliary data structures."

All 6 Follow-Up Variants You Must Know

The stock problem is actually a series of 6 LeetCode problems. Interviewers at companies like Goldman Sachs, Google, and Amazon commonly ask follow-ups after you solve #121. Know all of them.

Variant 1: Multiple Transactions Allowed (LeetCode #122)

"What if you could buy and sell on multiple days?"

Insight: Capture every upward slope. Add price - prev_price to profit whenever it's positive.

Python — LC #122
def maxProfit(prices):
    profit = 0
    for i in range(1, len(prices)):
        if prices[i] > prices[i - 1]:
            profit += prices[i] - prices[i - 1]
    return profit

Variant 2: At Most 2 Transactions (LeetCode #123)

"What if you could complete at most 2 transactions?"

Insight: Track four states — after 1st buy, after 1st sell, after 2nd buy, after 2nd sell. Use DP with these states.

Python — LC #123
def maxProfit(prices):
    buy1 = buy2 = float('-inf')
    sell1 = sell2 = 0

    for price in prices:
        buy1  = max(buy1,  -price)
        sell1 = max(sell1,  buy1  + price)
        buy2  = max(buy2,   sell1 - price)
        sell2 = max(sell2,  buy2  + price)

    return sell2

Variant 3: At Most K Transactions (LeetCode #188)

"What if you could complete at most k transactions?"

Insight: Generalize the 2-transaction DP to k states. Use a DP table of size k x n. Handle the edge case k >= n//2 (equivalent to unlimited transactions).

Variant 4: With Cooldown (LeetCode #309)

"What if after selling, you must wait 1 day before buying again?"

Insight: Track three states — held (stock in hand), sold (just sold today), rest (cooldown or idle). Transition between states each day.

Variant 5: With Transaction Fee (LeetCode #714)

"What if each transaction incurs a fee?"

Insight: Same as unlimited transactions (#122) but subtract fee on each sell. Two states: cash (no stock held), held (holding stock).

Variant 6: Can You Prove the Greedy? (LC #122 Follow-Up)

"Can you prove the greedy is optimal for unlimited transactions?"

Insight: Any multi-day profit can be decomposed into sum of consecutive daily gains. Capturing every positive day-to-day delta is equivalent to any optimal multi-buy strategy.

📋 Stock Problem Series — Quick Reference

ProblemConstraintApproach
LC #1211 transactionGreedy — track min price
LC #122UnlimitedGreedy — sum all positives
LC #123At most 2DP — 4 states
LC #188At most kDP — k × n table
LC #309With cooldownDP — 3 states
LC #714With feeDP — 2 states

The Bigger Pattern: When to Use This Technique

The "track minimum/maximum so far" pattern from LC #121 appears across many other problems. Once you internalize it here, you'll recognize it immediately elsewhere.

💡 The Pattern in One Sentence

When a problem asks you to find an optimal pair (i, j) where i < j and some function of arr[i] and arr[j] is maximized — consider tracking the best arr[i] seen so far as you scan right, so each arr[j] can be evaluated in O(1).

How to Actually Remember This

With 199 appearances across 63 companies, you will encounter this problem or one of its 6 variants in a real interview. Seeing the code once isn't enough — you need to retrieve it fluently under pressure.

✅ Your Spaced Repetition Schedule

  1. Today (Day 0): Solve LC #121 from scratch without this guide. Then solve LC #122 (unlimited transactions).
  2. Day 3: Re-solve both without looking at code. Draw the walkthrough table on paper.
  3. Day 7: Solve LC #123 (at most 2 transactions). Explain the 4-state DP out loud.
  4. Day 14: Solve LC #309 (cooldown) and LC #714 (fee). Connect them back to #121.
  5. Day 30: Pick a random stock variant cold. Solve it in under 20 minutes.

Track all 6 stock variants together on DSAPrep.dev. Tag them under Greedy and Dynamic Programming and the spaced repetition system will surface them at exactly the right intervals — so you don't review too early (wasted effort) or too late (already forgotten).

Track All 6 Stock Variants on DSAPrep.dev →

One problem at a time. One review at a time. Future you will thank present you.