A greedy algorithm commits to the locally best choice and never backtracks. It is correct only for some problems. Interval scheduling (finish first) is the classic yes.
Goal
Schedule non-overlapping kiosk slots by earliest finish time.
Earliest finish
slots = [
("Ada", 9, 11),
("Bena", 10, 12),
("Caleb", 11, 13),
("Dina", 8, 9),
]
slots = sorted(slots, key=lambda s: s[2])
chosen = []
end = -1
for name, start, finish in slots:
if start >= end:
chosen.append(name)
end = finish
print("order by finish", [s[0] for s in slots])
print("chosen", chosen)Dina 8–9, Ada 9–11, Caleb 11–13. Bena overlaps Ada.
A greedy that can fail
# Coin change: greedy largest-first is wrong for some denoms.
def greedy_coins(amount, denoms):
denoms = sorted(denoms, reverse=True)
used = []
for d in denoms:
while amount >= d:
amount -= d
used.append(d)
return used, amount
print(greedy_coins(30, [1, 10, 25]))
print(greedy_coins(30, [1, 5, 10, 20]))For 1, 10, 25, greedy on 30 takes 25+… and may use more coins than 10+10+10. Next chapter’s DP finds the fewest coins.
Pitfall
“Greedy feels right” is not a proof. If you cannot state the invariant (here: the leftover interval stays schedulable), write DP or search instead.