Greedy Algorithms: Code Templates in 6 Languages
If you have not read the concept guide yet, start there for the greedy choice property and the intuition. This page gives you templates you can adapt during an interview. The main template runs in
Main Template: Furthest-Reach Scan
This is the greedy loop in its purest form. One state variable holds the best outcome reachable so far. Each element either extends it or does not. The template decides, for every index, whether the jumps seen so far can still reach it.
Use this for Jump Game .
function canJump(nums) {
// maxReach is the furthest index we can get to
// with the jumps we have seen so far.
let maxReach = 0;
for (let i = 0; i < nums.length; i++) {
// If the current index is beyond our reach, a gap
// exists and the last index is unreachable.
if (i > maxReach) return false;
// The greedy decision: keep the largest reach.
// No other jump can improve on this value.
maxReach = Math.max(maxReach, i + nums[i]);
}
return true;
}Code Breakdown
Key Variables
nums: the input. For this template, each value is a maximum jump length from its index.maxReach: the state variable. It holds the furthest index reachable with the jumps examined so far. It never shrinks.
Visual Mechanism
graph TD
A["maxReach = 0"] --> B["For each index i"]
B --> C{"i > maxReach?"}
C -->|Yes| D["Return false: a gap exists"]
C -->|No| E["maxReach = max(maxReach, i + nums[i])"]
E --> F{"More elements?"}
F -->|Yes| B
F -->|No| G["Return true"]
Critical Sections
The initialization sets maxReach to 0 because index 0 is always reachable by definition. No other starting value is needed.
The greedy decision is the update. Extending the reach is never worse than any alternative, because a larger reach only adds options. This is the greedy choice property applied to a single state variable.
The failure check is where the correctness lives. If an index lies beyond maxReach, no future jump can help, since every later jump starts from a position that is itself unreachable. The loop ends with true only when no such gap appears.
The same shape, with a different state variable, solves Best Time to Buy and Sell Stock II . There the state is the running profit, and the greedy decision adds each price rise as it appears.
Variations
1. Sort-Then-Scan Selection
Use this when the input must be ordered before the greedy pass can work. The sort key is the greedy choice itself, so picking the wrong key breaks the whole approach.
Use this for interval-style problems. The full treatment lives on the interval scheduling pages.
def select_max(intervals):
# Sorting by end time is the greedy choice. The
# earliest-finishing interval leaves the most room
# for everything after it.
intervals.sort(key=lambda x: x[1])
selected = []
last_end = float('-inf')
for start, end in intervals:
# Take the interval when it does not overlap the
# last selection, then advance the boundary.
if start >= last_end:
selected.append((start, end))
last_end = end
return selected2. Greedy Matching
Use this when two sorted collections must be paired and every pair is independent. The smallest useful cookie, the cheapest available unit, the lightest feasible load. The rule is always to consume the least that satisfies the need.
Use this for Assign Cookies .
def find_content_children(g, s):
g.sort()
s.sort()
child = 0
cookie = 0
while child < len(g) and cookie < len(s):
# Give the child the smallest cookie that works.
# A bigger cookie would not help anyone else more.
if s[cookie] >= g[child]:
child += 1
cookie += 1
return child3. Frequency-Block Scheduling
Use this when items must be spaced apart by a cooldown and the most frequent item decides the pace. The answer is a formula, not a simulation.
Use this for Task Scheduler .
def least_interval(tasks, n):
freq = [0] * 26
for task in tasks:
freq[ord(task) - ord('A')] += 1
max_freq = max(freq)
# Tasks tied for the most frequent each occupy
# one slot in the final frame.
max_count = freq.count(max_freq)
# max_freq - 1 full frames of n + 1 slots, plus the
# tail frame that holds the tied tasks.
return max(len(tasks), (max_freq - 1) * (n + 1) + max_count)4. Two-Pass Comparison
Use this when the constraint has two directions and each element must satisfy both neighbors. One pass handles the left direction, the second pass the right direction.
Use this for Candy .
def candy(ratings):
n = len(ratings)
candies = [1] * n
# Left pass: a child rated higher than the neighbor
# on the left must get more candy than that neighbor.
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
candies[i] = candies[i - 1] + 1
# Right pass: do the same from the right. The max
# keeps the left-pass constraint intact.
for i in range(n - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
candies[i] = max(candies[i], candies[i + 1] + 1)
return sum(candies)Now head to the practice problems to apply these templates to real interview questions.