🎉 All 30 days are live — the full DSA-30 course, from Big-O to System Design. See the roadmap →

Classic Greedy Problems

Half the “medium greedy” problems you’ll ever see are one of about seven textbook problems in a costume — and every one of them reduces to the same four-line shape: sort, scan, commit or skip, return. The only thing that changes is what you sort by. Find that sort key and the code is mechanical; miss it and you don’t have a greedy solution at all. This page is the seven, and the sort key for each.

A guided tour of the greedy problems every algorithms textbook covers — and the templates they produce that show up over and over in interviews. Once you’ve seen these, half the LeetCode “medium greedy” tag falls into one of these shapes.

1. Activity Selection

Given n activities with start and end times, select the maximum number that don’t overlap.

Greedy: sort by earliest end time. Iterate, picking each activity whose start time is the previous end time.

Recall the scan — the one condition that decides whether to take an activity:

python · fill in the blanks0/1 hints
def activity_selection(activities):
activities.sort(key=lambda a: a[1]) # sort by EARLIEST END TIME
result, last_end = [], 0
for start, end in activities:
# ??? can we take this activity without overlap?
result.append((start, end))
last_end = end
return result

Time: O(n log n) for the sort. Space: O(1) extra.

We proved this correct via the exchange argument on the previous page.

Template-mates: Non-overlapping Intervals, Minimum Number of Arrows to Burst Balloons, Erase Overlap Intervals, Meeting Rooms — all sort-by-end-time problems.

2. Fractional Knapsack

You have items with weights and values, and a knapsack with capacity W. You can take fractions of items (cut them into smaller pieces). Maximize total value.

(Note: this is the fractional version. The 0/1 version — take-or-leave whole items — is DP, not greedy.)

Greedy: sort by value-per-weight ratio (descending). Take items whole until you can’t fit any more whole; then take a fraction of the next.

Time: O(n log n). Space: O(1).

Why it works (exchange argument): suppose an optimal solution skips an item with the best ratio and takes one with a worse ratio instead. Replace that worse item with an equivalent weight of the better-ratio one — total value goes up. So optimal must include the best-ratio item first.

Predict first
Fractional knapsack is solved greedily (sort by value/weight ratio and fill). Why does the SAME greedy fail for 0/1 knapsack, where you must take whole items?
⚠️

The fractional version is greedy. The 0/1 version is DP. This is a famous distinction. With fractions allowed, the greedy ratio argument works because you can always “fill the remaining capacity perfectly.” Without fractions, you might be forced to skip the highest-ratio item if it doesn’t fit, and then greedy can give a suboptimal answer.

3. Huffman Coding

Given character frequencies, design a binary prefix code such that the total encoded length is minimized.

Greedy: repeatedly merge the two least-frequent characters/trees into a new internal node. The resulting tree is the optimal prefix code.

import heapq
 
def huffman_codes(frequencies):
    # frequencies = {char: freq}
    heap = [[freq, char] for char, freq in frequencies.items()]
    heapq.heapify(heap)
    while len(heap) > 1:
        lo = heapq.heappop(heap)            # two smallest
        hi = heapq.heappop(heap)
        # Merge — assign '0' to the left subtree, '1' to the right
        for pair in lo[1:]: pair[1] = '0' + pair[1] if isinstance(pair, list) else pair
        for pair in hi[1:]: pair[1] = '1' + pair[1] if isinstance(pair, list) else pair
        heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
    return heap[0]

Time: O(n log n) — n heap operations of O(log n) each. Space: O(n).

Why it works (exchange argument): in any optimal prefix tree, the two deepest leaves must be the two least-frequent characters. (Otherwise we could swap them with deeper characters that have higher frequencies and reduce the total length.) So merging the two smallest first is always part of an optimal solution.

This is the foundation of DEFLATE (gzip), JPEG, MP3, and dozens of compression formats. We covered it briefly in Day 7 — Heap Applications.

4. Interval Scheduling Maximization

Same shape as activity selection — given intervals, pick the maximum non-overlapping subset.

This is literally activity selection in disguise. Same greedy: sort by end time, pick the next one that fits.

Variations:

  • Minimum Number of Arrows to Burst All Balloons — sort by end coordinate, count “arrow shots” needed where each shot ends where the current overlap ends.
  • Non-overlapping Intervals — count how many to remove to leave a non-overlapping set. Equivalently, n minus the activity-selection answer.
  • Erase Overlap Intervals — same as Non-overlapping.

All four problems use the same code with tiny adjustments. Pattern: sort by end, sweep, count.

5. Job Sequencing with Deadlines

You have n jobs, each with a profit and a deadline. Each job takes 1 unit of time. You can do one job at a time. Maximize total profit by choosing which jobs to do and in what order.

Greedy: sort by profit (descending). For each job, schedule it in the latest possible time slot before its deadline that’s still free.

Time: O(n²) for the simple version (O(n log n) with a Disjoint Set Union for slot allocation). Space: O(d) where d is the max deadline.

Why it works: if we sort by profit and place each job in the latest available slot, we maximize the chance later (less profitable) jobs can still be scheduled in earlier slots.

6. Minimum Spanning Trees — preview

The MST problem (find a minimum-weight tree connecting all vertices in a weighted graph) has two greedy algorithms:

  • Kruskal’s algorithm: sort edges by weight ascending. Add each edge if it doesn’t create a cycle. Stop when you have V − 1 edges.
  • Prim’s algorithm: start from any vertex. Grow the tree one vertex at a time, always adding the cheapest edge that connects a new vertex.

Both work. Both are O(E log V). Both have rigorous correctness proofs via exchange arguments on edge weights (the cut property and the cycle property of MSTs).

We cover these in detail on Day 22 — Minimum Spanning Trees. For now, just know: MST is greedy, and there are two equally valid ways to be greedy about it.

7. Dijkstra’s Shortest Path — preview

Find the shortest path from a source vertex to every other vertex in a weighted graph (with non-negative weights).

Greedy: repeatedly pick the unvisited vertex with the smallest tentative distance. Relax its outgoing edges. Mark visited. Repeat.

Dijkstra is a priority-queue-driven greedy algorithm. Each iteration commits to the closest unvisited vertex — and never reconsiders that decision. This works precisely because edge weights are non-negative; with negative weights, the greedy commitment can be wrong, and you need Bellman-Ford instead.

Full coverage on Day 21 — Shortest Paths.

The unifying pattern

Notice the shape that recurs:

1. SORT by some criterion (the greedy ordering).
2. ITERATE through the sorted items.
3. For each item, COMMIT or SKIP based on a simple rule.
4. RETURN the accumulated answer.

The whole skill of greedy is finding the right sort criterion. That’s the insight. Once you have it, the code is mechanical.

Common sort criteria:

Problem familySort by
Interval scheduling / overlapEarliest end time
Fractional knapsack / packingValue-per-weight (descending)
Job sequencing with deadlinesProfit (descending)
Huffman codingLowest two frequencies (via heap)
MST (Kruskal)Edge weight (ascending)
DijkstraTentative distance (via min-heap)
Minimum platforms / meeting roomsStart time (then end time as secondary)
Patching arrays / coin change canonicalLargest denomination ≤ remaining

If you see a problem that smells greedy, the first question is always: “what’s the right sort order?”

Quick check

Across activity selection, fractional knapsack, job sequencing, and Kruskal's MST, what is the single most important decision that determines whether your greedy is correct?

You’ve seen the canonical greedy shapes and their sort keys. Next: greedy vs DP — how to tell, on a new problem, whether the greedy choice is safe or whether you need DP’s “try everything and memoize.”

Finished this page?