Intermediate: core algorithms · Heaps and greedy algorithms · topic 38 of 61 · 35 min
Greedy algorithms: the idea
Explanation
What "greedy" means
A greedy algorithm makes one choice at a time, always taking the option that looks best at that moment (the most valuable, the cheapest, the earliest to finish), and never undoes a choice. Because it never looks back, it is usually just a sort plus one pass: O(n log n) time.
The catch: a locally best step does not always lead to the best overall answer. So with greedy there are always two questions: what is the greedy rule, and why is it correct? In interviews, giving a short reason, or a counterexample for a wrong rule, matters as much as the code.
When greedy works
- Greedy-choice property: there is always an optimal answer that includes the greedy first choice. So taking it never hurts.
- Optimal substructure: after the greedy choice, what is left is a smaller problem of the same kind, and an optimal answer to it, plus the first choice, is optimal overall.
The usual way to argue the first point is an exchange argument. Take ANY optimal answer. If it does not use the greedy choice, swap one of its choices for the greedy one and show the answer is still valid and no worse. Then an optimal answer with the greedy choice exists.
Example 1: the most meetings in one room
A single meeting room gets requests (start, end). Choose the largest number of meetings that do not overlap; a meeting may start exactly when the previous one ends. This is the activity selection problem. The greedy rule: sort by END time, and take each meeting that starts at or after the end of the last one taken.
def max_meetings(meetings):
count, last_end = 0, float("-inf")
for start, end in sorted(meetings, key=lambda m: m[1]):
if start >= last_end:
count += 1
last_end = end
return count
print(max_meetings([(1, 4), (3, 5), (0, 6), (5, 7), (8, 9), (5, 9)])) # Output: 3Why the earliest end? Exchange argument: let the meeting that ends first be g. Any optimal schedule has some first meeting f. Since g ends no later than f, replace f with g: g still ends before the second meeting starts, so nothing overlaps and the count is the same. Taking g is therefore safe, and the rest is the same problem on the meetings that start after g ends.
Other rules that sound sensible are wrong. "Earliest start first" can pick one long meeting (0, 10) and block (1, 2), (3, 4) and (5, 6). "Shortest first" picks (4, 7) from (1, 5), (4, 7), (6, 10) and blocks both others, which fit together.
Example 2: change with Indian notes and coins
To pay an amount with the fewest notes and coins of ₹1, 2, 5, 10, 20, 50, 100, 200 and 500, take the largest denomination that fits, again and again. ₹1,388 becomes 500 + 500 + 200 + 100 + 50 + 20 + 10 + 5 + 2 + 1: ten pieces. For this set the greedy answer is always the minimum, because each denomination relates well to the smaller ones (systems like this are called canonical). ATMs and cash counters follow the same idea, limited by what notes they hold.
NOTES = [500, 200, 100, 50, 20, 10, 5, 2, 1]
def make_change(amount):
used = []
for note in NOTES:
while amount >= note:
amount -= note
used.append(note)
return used
print(make_change(1388)) # Output: [500, 500, 200, 100, 50, 20, 10, 5, 2, 1]Where greedy fails: coins {1, 3, 4}
Now use coins of 1, 3 and 4 to make 6. Greedy takes 4, then 1, then 1: three coins. The best answer is 3 + 3: two coins. Taking the biggest coin first was a trap. With coins {5, 10} and amount 3, greedy gets stuck, and so does every method, because 3 cannot be made at all. For coin systems that are not canonical you need dynamic programming (Module 11), which tries every last coin and builds up answers for all smaller amounts.
Common mistakes
- Using greedy because it "feels right" without a reason. Fix: give an exchange argument or test small cases against brute force.
- Sorting meetings by start time or by length. Fix: sort by end time for maximum non-overlapping meetings.
- Getting the touching rule wrong: if back-to-back meetings are allowed, use start >= last_end; if not, use start > last_end. Ask.
- Assuming greedy change works for any coin system. Fix: it is correct for systems like Indian or US currency, not for {1, 3, 4}; use DP otherwise.
- Forgetting to sort a copy when the caller still needs the original order. Fix: sorted(items, key=...) returns a new list.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Greedy algorithms: the idea · 1/8
This lesson explains greedy algorithms: take the best choice now and never look back.
Prefer a video by a teacher? Find free videos on “greedy algorithm activity selection exchange argument coin change greedy fails” on YouTube ↗
In real life: Booking a shared conference room
An office has one board room and receives booking requests for tomorrow. The admin wants to approve as many meetings as possible. Sorting by end time and approving each request that starts after the last approved one ends gives the maximum number of meetings, and the exchange argument shows no other choice can do better. Payroll teams that pay salaries in cash use the greedy note count to plan how many notes of each kind to draw from the bank.
requests = [("Sales", 9, 11), ("HR", 10, 12), ("Tech", 11, 12), ("Design", 12, 13), ("Ops", 9, 13)]
approved, last_end = [], 0
for team, start, end in sorted(requests, key=lambda r: r[2]):
if start >= last_end:
approved.append(team)
last_end = end
print(approved)Practice (0/3 done)
Write min_notes_greedy(amount) that returns how many notes and coins are needed to pay amount (an integer ≥ 0) using the Indian denominations 500, 200, 100, 50, 20, 10, 5, 2 and 1, taking the largest that fits each time. Example: min_notes_greedy(1388) returns 10.
Write max_meetings(meetings) where meetings is a list of (start, end) with start < end. Return the largest number of meetings one room can hold with no overlap. A meeting MAY start exactly when the previous one ends. Use the greedy rule: sort by end time. Example: max_meetings([(1, 4), (3, 5), (0, 6), (5, 7), (8, 9), (5, 9)]) returns 3.
Write greedy_vs_optimal(coins, amount). coins is a list of distinct positive integers in any order; amount ≥ 0. Return a tuple (greedy_count, optimal_count). greedy_count: repeatedly take the largest coin that fits; if you get stuck with something left, it is -1. optimal_count: the true minimum number of coins (use a small dynamic programming table over 0..amount); -1 if the amount cannot be made. Example: greedy_vs_optimal([1, 3, 4], 6) returns (3, 2).
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Interval problemsHeaps and greedy algorithms · same module
- Heaps and priority queuesHeaps and greedy algorithms · same module
- 1-D DP classicsDynamic programming
- Meetings and video callsSpoken English: Zero to Fluent · English at work and interviews
- Recursion and sorting algorithmsPython Programming · Algorithms, data structures and testing
- Regular expressions in practiceJavaScript Programming (language in depth) · Map, Set, iterators, generators and regular expressions
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does a greedy algorithm do at each step?
Answer: Takes the choice that looks best now and never undoes it
Greedy commits to the locally best choice. Trying everything is brute force; storing sub-answers is dynamic programming.
For the most non-overlapping meetings in one room, sort by…
Answer: end time
The meeting that ends first leaves the most room for the rest; the exchange argument proves it.
Coins {1, 3, 4} and amount 6: how many coins do greedy and the best answer use?
Answer: Greedy 3, best 2
Greedy takes 4 + 1 + 1. The best is 3 + 3.
What does an exchange argument show?
Answer: That swapping a choice in any optimal answer for the greedy choice keeps it valid and no worse
So an optimal answer containing the greedy choice exists, and taking it is safe.
Meetings (1, 5), (4, 7), (6, 10). What does "shortest meeting first" pick, and what is the best?
Answer: It picks 1, the best is 2
Shortest is (4, 7), which overlaps both others. Taking (1, 5) and (6, 10) gives 2.
Coins [1, 3, 4], amount 6. What does "always take the largest coin that fits" give, and what is optimal?Related
Answer: 3 coins and 2 coins
Greedy takes 4, 1, 1 (three coins); 3 + 3 needs only two. That is why DP is needed.
From 1-D DP classics
In the minimum meeting rooms solution, what does the min-heap store?Related
Answer: End times of meetings currently using a room
The root is the room that frees up first; if it is free by the next start, that room is reused.
From Interval problems
Why does the fractional knapsack greedy fail on the 0/1 knapsack?Related
Answer: Because whole items can leave unused space, so the best value per kilo is not always best overall
With capacity 50, greedy takes 60 + 100 = 160 and cannot fit the 30 kg item; 100 + 120 = 220 is better.
From More greedy classics
Which is a good way to start a meeting?Related
Answer: Shall we get started?
"Shall we get started?" is a friendly, natural way to begin.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Heaps and greedy algorithms” before its exam: Interval problems, More greedy classics.
- Module 8 exam: Heaps and greedy algorithms · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Graphs. 61 topics left in the course before the final exam.