Greedy
Greedy algorithms make the locally optimal choice at each step, expecting it to lead to the globally optimal solution. Master reachability scanning, circular deficit tracking, greedy grouping, and partition extension to solve problems that reward choosing the best immediate option.
Learn & Reference
Understanding the Pattern
Greedy Pattern
Greedy algorithms build solutions piece by piece, always choosing the option that looks best right now. Unlike DP, which considers all possibilities, greedy commits to each choice and never backtracks.
Greedy: Choose best now → move forward → never reconsider
DP: Consider all options → pick the best overall
The key question: does the locally optimal choice lead to the globally optimal solution? If yes, greedy works and is usually much simpler and faster than DP.
When Greedy Works
A problem has the greedy choice property if making the locally optimal choice at each step produces a globally optimal solution. Two ways to verify:
- Greedy stays ahead: Show that after each step, the greedy solution is at least as good as any other.
- Exchange argument: If an optimal solution differs from greedy at some step, show that swapping to the greedy choice doesn't make it worse.
Five Core Techniques
1. Greedy Reachability
Track the farthest position you can reach. Scan left to right, extending the frontier.
nums = [2, 3, 1, 1, 4]
i=0: farthest = max(0, 0+2) = 2
i=1: farthest = max(2, 1+3) = 4 ← reached end!
→ true
When to use: "Can you reach the end?", "Is it possible to get from A to B?"
2. BFS-Level Greedy
Treat the problem like BFS where each "level" is one decision. Track the current level boundary and the farthest you can reach from this level.
nums = [2, 3, 1, 1, 4]
Level 0: i=0, reach [1,2] → jumps = 1, currentEnd = 2
Level 1: i=1,2, reach [4] → jumps = 2, reached end!
→ 2 jumps
When to use: "Minimum number of steps/jumps to reach the end"
3. Circular Deficit Tracking
For circular problems, compute the running surplus/deficit. The optimal starting point is just after the deepest deficit.
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
diff = [-2,-2,-2, 3, 3] total = 0 (feasible)
Running sum: -2, -4, -6, -3, 0
↑ deepest deficit at i=2
→ start at i=3
When to use: "Circular route", "starting point on a loop", "can you complete the circuit?"
4. Greedy Grouping from Minimum
Always start building groups from the smallest available element. Correctness: the smallest element must belong to some group, and its group must start with it.
hand = [1, 2, 3, 6, 2, 3, 4, 7, 8], groupSize = 3
Sorted: [1, 2, 2, 3, 3, 4, 6, 7, 8]
Group from 1: [1, 2, 3] ✓
Group from 2: [2, 3, 4] ✓
Group from 6: [6, 7, 8] ✓
→ true
When to use: "Divide into consecutive groups", "arrange in sets of K"
5. Greedy Partitioning
Scan left to right, extending each partition's boundary to include all occurrences of its characters. Close the partition at the earliest valid point.
s = "ababcbacadefegdehijhklij"
Last index: a→8, b→5, c→7, d→14, e→15, f→11, ...
Scan: extend to 8, then 5, 7, 5, 7, 5, 8, 7, 8 → close at 8 (size 9)
extend to 14, 15, 11, 15, 14, 14, 15 → close at 15 (size 7)
...
→ [9, 7, 8]
When to use: "Partition string so each letter appears in at most one part", "maximum number of segments"
Common Mistakes
- Applying greedy when DP is needed — Greedy fails for Coin Change with arbitrary denominations (e.g., coins [1, 3, 4], amount 6: greedy picks 4+1+1=3 coins, optimal is 3+3=2 coins). Always verify the greedy choice property.
- Not sorting when order matters — Hand of Straights and many interval problems require sorting first. Greedy on unsorted input gives wrong answers.
- Off-by-one in reachability — When checking
farthest >= n-1(notn), and iteratingi < n-1(notn) to avoid checking past the goal. - Circular array confusion — In gas station, when the tank goes negative, reset start to
i+1(noti). The current station is proven bad, the next one might work. - Confusing "can reach" with "minimum steps" — Jump Game (reachability) and Jump Game II (BFS-level) look similar but use different greedy strategies. Don't mix them up.
When to Use
Template
1# --- Greedy Reachability Template ---2def solve(nums):3farthest = 04for i in range(len(nums)):5if i > farthest:6return False # Can't reach this position7farthest = max(farthest, i + nums[i])8return True # Can reach the end910# --- Greedy Scan Template ---11def greedy_scan(items):12result = 013# Sort if order matters14# items.sort()1516for item in items:17# Make the locally optimal choice18# Update result / state19pass2021return result
Syntax Reference
1# --- Sorting ---2nums.sort() # In-place sort O(n log n)3sorted_nums = sorted(nums) # New sorted list45# --- Counting ---6from collections import Counter7count = Counter(nums) # {val: freq}8count[val] -= 1 # Decrement9if count[val] == 0: del count[val] # Remove zero entries1011# --- Greedy scan ---12farthest = 013for i in range(len(nums)):14if i > farthest: return False # Stuck15farthest = max(farthest, i + nums[i])1617# --- Min / Max ---18farthest = max(farthest, i + nums[i])19total = sum(nums)
Common Recipes
1# Recipe 1: Greedy Reachability (Jump Game)2def can_jump(nums):3farthest = 04for i in range(len(nums)):5if i > farthest:6return False7farthest = max(farthest, i + nums[i])8return True910# Recipe 2: Circular Deficit (Gas Station)11def can_complete_circuit(gas, cost):12total_tank = 013curr_tank = 014start = 015for i in range(len(gas)):16diff = gas[i] - cost[i]17total_tank += diff18curr_tank += diff19if curr_tank < 0:20start = i + 121curr_tank = 022return start if total_tank >= 0 else -12324# Recipe 3: Greedy Grouping (Hand of Straights)25def is_n_straight_hand(hand, group_size):26if len(hand) % group_size != 0:27return False28count = Counter(sorted(hand))29for card in sorted(count):30while count[card] > 0:31for i in range(card, card + group_size):32if count[i] <= 0:33return False34count[i] -= 135return True
Complexity
| Technique | Time | Space | Example |
|---|---|---|---|
| Reachability scan | O(n) | O(1) | Jump Game |
| BFS-level greedy | O(n) | O(1) | Jump Game II |
| Circular deficit | O(n) | O(1) | Gas Station |
| Greedy grouping | O(n log n) | O(n) | Hand of Straights |
| Greedy partitioning | O(n) | O(1) | Partition Labels |
Watch Out
- ✗Applying greedy when DP is needed — Greedy fails for Coin Change with arbitrary denominations (e.g., coins [1, 3, 4], amount 6: greedy picks 4+1+1=3 coins, optimal is 3+3=2 coins). Always verify the greedy choice property.
- ✗Not sorting when order matters — Hand of Straights and many interval problems require sorting first. Greedy on unsorted input gives wrong answers.
- ✗Off-by-one in reachability — When checking `farthest >= n-1` (not `n`), and iterating `i < n-1` (not `n`) to avoid checking past the goal.
- ✗Circular array confusion — In gas station, when the tank goes negative, reset start to `i+1` (not `i`). The current station is proven bad, the next one might work.
- ✗Confusing "can reach" with "minimum steps" — Jump Game (reachability) and Jump Game II (BFS-level) look similar but use different greedy strategies. Don't mix them up.