Theory · Greedy
DSA Roadmap Open Visualizer Back to intro

Algocraft · Theory

Greedy: commit to the best local choice, never look back

A greedy algorithm makes one irrevocable decision at a time, always picking whatever looks best right now, and never revisits that decision once it's made. Writing the algorithm is usually the easy part — a handful of lines. The real challenge is proving that never reconsidering earlier choices still lands you on a globally optimal answer. This page covers the two greedy problems on this site — Interval Scheduling and Huffman Coding — plus the mindset for spotting when greedy is safe to use at all.

01 · Foundation

What makes greedy risky

A greedy algorithm is really a bet. At every step it assumes that whichever option looks best right now can never be beaten later by some smarter combination of choices it passed up. Sometimes that bet is correct. Sometimes it isn't — and when it isn't, greedy produces an answer that's valid but not optimal, with no way to go back and fix it.

A classic counterexample makes this concrete. Say you're making change with coin denominations [1, 3, 4] for amount 6. Greedy always grabs the largest coin that still fits:

That's 4 coins — but 3 + 3 = 6 only needs 2 coins. Greedy's very first move (grabbing the 4) locked in a decision that a smarter combination later beat. It had no way to know that in advance, and no way to undo it once made.

This is exactly why Coin Change on this site is solved with Dynamic Programming (see the DP theory page), not greedy. Greedy only works when a problem's structure specifically guarantees the locally-best choice is safe — and Coin Change, in general, doesn't guarantee that.

02 · Recognizing greedy

When greedy DOES work — the two properties

Greedy is only safe to use when a problem has both of the following, and — unlike DP — you need to actually convince yourself they hold before you trust the algorithm:

The difference from DP is subtle but important. DP explores and compares multiple options at each step because it can't yet prove which one is safe — so it keeps every candidate around and lets the recurrence sort it out. Greedy has already proven, for this specific problem, that comparing is unnecessary: the locally-best choice is always safe, so there's nothing to backtrack over and nothing to keep in reserve.

Interval Scheduling is the cleanest place to see this. Given a set of overlapping intervals, sort by earliest finish time and greedily accept any interval that starts at or after the end of the last one you accepted:

0 1 3 4 5 6 7 8 9 10 11 (1,4) ✓ selected (3,5) ✗ overlaps (0,6) ✗ overlaps (5,7) ✓ selected (3,9) ✗ overlaps (5,9) ✗ overlaps (6,10) ✗ overlaps (8,11) ✓

Eight candidate intervals, sorted top-to-bottom by end time. Green bars are what greedy selects — (1,4), (5,7), (8,11) — because each one starts at or after the previous selection's end. Muted bars overlap an already-selected interval and are rejected; that rejection is never revisited.

03 · Method

A repeatable framework

Use this sequence whenever you suspect a problem might be a greedy candidate — including the step where you actively try to break it, since that's the step people skip.

  1. 1 Define the criterion, and sort by itIdentify what makes "locally best" well-defined, then sort the input by that key — earliest end time for Interval Scheduling, lowest frequency for Huffman.
  2. 2 Commit at each step, never revisitAt every step, take the locally best available option and move on. There's no reconsidering a past decision later in the same pass.
  3. 3 Try to break it before you trust itActively look for a counterexample where an earlier greedy choice blocks a better overall answer. If you can construct one, greedy is wrong for this problem and you need DP instead.
  4. 4 If it's safe, keep the code shortOnce the greedy-choice property is confirmed, the implementation is usually a sort plus a single linear pass — no recursion, no memo table.
  5. 5 Reach for a supporting data structureGreedy often needs help making each step efficient: a min-heap for Huffman's "always merge the two least-frequent items," union-find for Kruskal's cycle check (Kruskal is covered on the Graphs theory page).

04 · Worked example

Interval Scheduling, step by step

Intervals (start, end): (1,4), (3,5), (0,6), (5,7), (3,9), (5,9), (6,10), (8,11). Sort by end time, then walk through in that order: accept an interval only if its start is at or after the end of the most recently accepted interval.

IntervalEnd timeDecisionWhy
(1,4)4AcceptedFirst candidate — nothing selected yet. Last end → 4.
(3,5)5RejectedStarts at 3, before last end 4 → overlaps (1,4).
(0,6)6RejectedStarts at 0, before last end 4 → overlaps (1,4).
(5,7)7AcceptedStarts at 5, ≥ last end 4 → no overlap. Last end → 7.
(3,9)9RejectedStarts at 3, before last end 7 → overlaps (5,7).
(5,9)9RejectedStarts at 5, before last end 7 → overlaps (5,7).
(6,10)10RejectedStarts at 6, before last end 7 → overlaps (5,7).
(8,11)11AcceptedStarts at 8, ≥ last end 7 → no overlap. Last end → 11.

Final selection: (1,4), (5,7), (8,11) — 3 non-overlapping intervals, which is the maximum possible for this input. Notice the greedy choice never got revisited: once (1,4) was accepted, every later decision was made purely by comparing against it, never by reopening it.

Open the Interval Scheduling visualizer to watch this exact selection animate.

05 · Worked example

Huffman Coding, briefly

Huffman Coding builds an optimal prefix-free binary code for a set of characters, using their frequencies. The greedy construction:

Put every character in a min-heap keyed by frequency. Repeatedly remove the two lowest-frequency nodes, merge them into a new internal node whose frequency is their sum, and push that merged node back into the heap. Repeat until exactly one node remains — the root.

The greedy choice here is always "merge whichever two nodes currently have the smallest frequencies" — never anything else, and never reconsidered once merged. That single rule is what gives the resulting tree its shape: characters that are frequent tend to get merged later, once larger combined nodes dominate the heap, which keeps them closer to the root and gives them shorter codes. Characters that are rare get merged early, while frequencies are still small, which pushes them deeper into the tree and gives them longer codes — exactly the trade-off an optimal prefix code should make.

See the Huffman Coding visualizer for the full merge sequence animated on real character frequencies.

06 · Complexity

How to reason about time

Greedy algorithms tend to be dominated by whatever sort or heap operations set up the ordering, with the actual greedy pass itself being cheap and linear.

Dijkstra's, Prim's, and Kruskal's algorithms are greedy too, but their complexity depends on the graph representation and heap choice — that's covered on the Graphs theory page rather than repeated here.

07 · Before you start coding

Common mistakes

08 · Try it yourself

The two greedy visualizers on this site