Lesson 2 — Greedy: Carry the Best-So-Far

~20 minutes · Day 1 · greedy min-tracking · Python

Tonight's problem is Best Time to Buy and Sell Stock. It is an interview classic because it looks like finance, but the useful skill is simpler: while scanning once, carry the best fact from the past so today is easy to judge.

The invariant

Best sell = today's price minus the cheapest price seen so far; one pass, carry the min.

That's the whole pattern. For each day, pretend today is the sell day. The only buy day worth considering is the cheapest earlier price. If you carry that one number, you never need to look backward again.

Key insight: carry only the smallest earlier price 7 1 5 3 6 4 day 0 day 1 day 2 day 3 day 4 day 5 sell today: 6 - cheapest earlier 1 = best profit 5 cheapest

The scan only needs the cheapest earlier price, then today's profit is one subtraction.

Best Time: the slow way first

The LeetCode statement gives one price per day and asks for the best profit from one buy before one sell. The obvious brute force is O(n²): try every earlier buy with every later sell.

def max_profit(prices):
    best = 0
    for buy in range(len(prices)):
        for sell in range(buy + 1, len(prices)):   # every valid later sale
            best = max(best, prices[sell] - prices[buy])
    return best

This works, but the inner loop keeps asking the same kind of question: “what was the cheapest buy before this sell?” A greedy algorithm keeps the locally best choice needed for the next step (Wikipedia: greedy algorithm). Here, that choice is the cheapest price so far.

Best Time: the greedy way

Walk left to right. At each price, first test the profit from selling today. Then update the cheapest price for future days. Python's max and min keep the two carried values small and explicit.

def max_profit(prices):
    cheapest = prices[0]                 # cheapest buy seen so far
    best = 0                             # best profit seen so far
    for price in prices[1:]:
        best = max(best, price - cheapest)   # sell today?
        cheapest = min(cheapest, price)      # new future buy?
    return best

The order matters for the story: today's sell uses the cheapest price from the past. After that, today can become the cheapest price for a later sell.

Sell check uses the past; update is for the future 1. sell today? price - cheapest 2. update min cheapest = min(...) later day uses new cheapest today cannot be its own earlier buy future sell

Check profit before updating the carried minimum, so the buy day stays earlier than the sell day.

Transfer: same pattern, new costume

Suppose you get daily temperatures and need the biggest rise from an earlier day to a later day. Before opening the answer: what single value should the loop carry?

Your prediction first — then reveal.
def biggest_rise(temps):
    coldest = temps[0]
    best = 0
    for temp in temps[1:]:
        best = max(best, temp - coldest)     # rise if today ends it
        coldest = min(coldest, temp)         # carry the best start
    return best

Same invariant: best ending today = today's value minus the smallest earlier value. Only the nouns changed.

What the trade buys you

ApproachTimeSpace
Brute force (try every buy and sell)O(n²)O(1)
Greedy min-tracking (carry cheapest so far)O(n)O(1)

The win is not a new data structure. It is the discipline to carry exactly the past fact the future needs.

Practice — answer before you scroll past

1. What value must the loop carry for this pattern?

2. Spot the bug in this loop:

cheapest = prices[0]
best = 0
for price in prices[1:]:
    best = max(best, price - cheapest)
    cheapest = max(cheapest, price)

3. Which problem has the same min-tracking shape?

4. Why is the brute force version slow?

Your move (tonight)

Solve Best Time to Buy and Sell Stock in your Notion tracker with the cycle — predict → attempt → compare → encode:

  1. Predict: say the brute force and the invariant out loud before coding.
  2. Attempt: write the one-pass version without looking back here.
  3. Compare: watch the source below only after your attempt.
  4. Encode: write the invariant in your own words in Notes.
One primary source

Watch NeetCode — Best Time to Buy and Sell Stock at the compare step. Listen for how he decides what value the loop must remember.


Stuck or curious? Ask your teacher (the agent) — that's what it's for.