Intermediate: core algorithms · Heaps and greedy algorithms · topic 40 of 61 · 40 min
More greedy classics
Explanation
Jump game: track the farthest reach
nums[i] is the longest jump you can make from index i. Starting at index 0, can you reach the last index? Trying every jump path is exponential. Instead, walk left to right and keep reach, the farthest index you can get to so far. If you ever stand on an index beyond reach, you are stuck. Otherwise update reach = max(reach, i + nums[i]). One pass: O(n) time and O(1) space.
def can_jump(nums):
reach = 0
for i, step in enumerate(nums):
if i > reach:
return False # this index cannot be reached
reach = max(reach, i + step)
return True
print(can_jump([2, 3, 1, 1, 4]), can_jump([3, 2, 1, 0, 4])) # Output: True FalseGas station: restart after a deficit
Stations stand on a circular road. Station i gives gas[i] litres, and driving to the next station costs cost[i]. Starting with an empty tank, from which station can you drive the full circle? Two facts give an O(n) answer. First, if total gas < total cost, no start works. Second, if you start at s and the tank goes negative on reaching station j + 1, then NO station between s and j can be a start either: each of them would arrive at j with even less fuel. So jump the start to j + 1 and reset the tank.
def can_complete_circuit(gas, cost):
if sum(gas) < sum(cost):
return -1
start = tank = 0
for i in range(len(gas)):
tank += gas[i] - cost[i]
if tank < 0:
start, tank = i + 1, 0
return start
print(can_complete_circuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])) # Output: 3Boats to save people: two pointers plus greedy
Each boat carries at most two people with total weight ≤ limit. What is the fewest boats? Sort the weights. The heaviest person needs a boat anyway; the best partner for them is the lightest person, because if even the lightest cannot fit, nobody can. So pair the two ends when they fit, otherwise send the heaviest alone, and move the pointers inward. Sorting costs O(n log n) and the pass O(n).
def boats(people, limit):
people = sorted(people)
i, j, count = 0, len(people) - 1, 0
while i <= j:
if people[i] + people[j] <= limit:
i += 1 # the lightest goes with the heaviest
j -= 1 # the heaviest always leaves
count += 1
return count
print(boats([3, 2, 2, 1], 3), boats([3, 5, 3, 4], 5)) # Output: 3 4Fractional knapsack: greedy works
A knapsack (bag) can carry capacity kilos. Each item has a value and a weight, and you may take any FRACTION of an item, like rice or spices sold loose. Sort items by value per kilo, highest first, and fill the bag in that order, taking a fraction of the last item that does not fit whole. The exchange argument: if an answer uses a kilo of a worse item while some of a better one is left, swapping that kilo raises the value. O(n log n).
def fractional(items, capacity): # items: (value, weight)
total = 0.0
for value, weight in sorted(items, key=lambda it: it[0] / it[1], reverse=True):
if capacity == 0:
break
take = min(weight, capacity)
total += value * take / weight
capacity -= take
return round(total, 2)
print(fractional([(60, 10), (100, 20), (120, 30)], 50)) # Output: 240.00/1 knapsack: greedy fails
If each item must be taken whole or not at all (a laptop, a phone), the same greedy rule breaks. With the items above and capacity 50, greedy by value per kilo takes the 10 kg and 20 kg items for 160 and then the 30 kg item does not fit. The best answer is the 20 kg and 30 kg items for 220. The 0/1 knapsack needs dynamic programming, which tries both "take" and "skip" for every item (Module 11).
Huffman coding: merge the two rarest
Huffman coding compresses text by giving frequent characters short bit codes and rare ones long codes. Put every character in a min-heap by frequency. Repeatedly pop the two rarest, join them under a new node whose frequency is their sum, and push it back. When one node is left, the path from it to each character gives that character's code, so characters merged early (the rare ones) end up deep, with long codes. Each of the n − 1 merges is O(log n): O(n log n). Formats such as ZIP's DEFLATE use Huffman codes as one step.
import heapq
def huffman_lengths(freq):
heap = [(f, i, {ch: 0}) for i, (ch, f) in enumerate(freq.items())]
heapq.heapify(heap)
counter = len(heap)
while len(heap) > 1:
f1, _, a = heapq.heappop(heap)
f2, _, b = heapq.heappop(heap)
merged = {ch: depth + 1 for ch, depth in {**a, **b}.items()}
heapq.heappush(heap, (f1 + f2, counter, merged))
counter += 1
return dict(sorted(heap[0][2].items()))
print(huffman_lengths({"a": 45, "b": 13, "c": 12, "d": 16, "e": 9, "f": 5}))
# Output: {'a': 1, 'b': 3, 'c': 3, 'd': 3, 'e': 4, 'f': 4}Common mistakes
- Jump game: returning True as soon as one jump looks good, or using BFS over all paths. Fix: one pass with the farthest reach.
- Gas station: trying every start, which is O(n²). Fix: total check plus the restart rule.
- Boats: pairing the two lightest people first. That can waste capacity. Fix: always handle the heaviest, paired with the lightest if they fit.
- Using the fractional greedy for the 0/1 knapsack. Fix: greedy only when items can be split; DP otherwise.
- Dividing by a weight of 0, or sorting by value instead of value per kilo. Fix: check the input and sort by value / weight.
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
More greedy classics · 1/8
This lesson works through the greedy classics you are most likely to meet in interviews.
Prefer a video by a teacher? Find free videos on “jump game gas station boats to save people fractional knapsack huffman coding greedy” on YouTube ↗
In real life: Loading a delivery van
A wholesale trader is filling a van with loose goods (rice, sugar, dal) to get the most value on a fixed weight limit. Because loose goods can be split, the fractional knapsack applies: load by value per kilo, best first. For boxed electronics that cannot be split, the trader needs the 0/1 version and a dynamic programming plan. The same greedy thinking is used in data compression, where Huffman codes give the most common symbols the shortest codes.
goods = [("rice", 2000, 50), ("cardamom", 3000, 2), ("dal", 1800, 30)] # name, value in Rs, kg
capacity = 40
for name, value, kg in sorted(goods, key=lambda g: g[1] / g[2], reverse=True):
take = min(kg, capacity)
capacity -= take
print(name, take, "kg")
if capacity == 0:
breakPractice (0/4 done)
Write can_jump(nums) where nums is a non-empty list of non-negative integers and nums[i] is the longest jump from index i. Starting at index 0, return True if you can reach the last index. One pass, O(n). Example: can_jump([2, 3, 1, 1, 4]) returns True and can_jump([3, 2, 1, 0, 4]) returns False.
Write can_complete_circuit(gas, cost). There are n stations on a circle; station i gives gas[i] and driving from i to i + 1 (and from the last back to 0) costs cost[i]. You start with an empty tank. Return the index of the station from which you can drive the whole circle once, or -1 if it is impossible. When a solution exists it is unique. O(n). Example: can_complete_circuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2]) returns 3.
Write num_rescue_boats(people, limit). people[i] is a person's weight (each ≤ limit). Each boat carries at most TWO people with total weight ≤ limit. Return the fewest boats needed for everyone. Use sorting plus two pointers. Do not change the input list. Example: num_rescue_boats([3, 2, 2, 1], 3) returns 3.
Write fractional_knapsack(items, capacity). items is a list of (value, weight) pairs with weight > 0, and you may take any fraction of an item. Return the largest total value that fits in capacity (≥ 0), rounded to 2 decimal places with round(x, 2). Example: fractional_knapsack([(60, 10), (100, 20), (120, 30)], 50) returns 240.0.
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
- Heap patterns: top-k and streamsHeaps and greedy algorithms · same module
- Knapsack problemsDynamic programming
- Counting and grouping with hashingHashing, two pointers and sliding window
- Indexes: making queries fastBackend Development · SQL in depth
- Fast and slow pointers and merging listsStacks, queues and linked lists
- 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.
In the jump game, what single number do you track while walking left to right?
Answer: The farthest index reachable so far
If the current index is beyond the farthest reach, it cannot be reached; otherwise reach may grow.
Gas station: starting at s, the tank first goes negative when reaching station j + 1. What can you conclude?
Answer: No station from s to j can be a valid start
Every station between s and j would arrive at j with no more fuel than the run from s had, so they all fail too.
Why does the fractional knapsack greedy fail on the 0/1 knapsack?
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.
In boats to save people, who should the heaviest person share a boat with?
Answer: The lightest person, if their total fits
If the lightest cannot fit with the heaviest, nobody can; if they can, pairing them saves the most room for others.
In Huffman coding, which two nodes are merged at each step?
Answer: The two least frequent
Merging the rarest first pushes them deepest, so they get the longest codes and frequent symbols get short ones.
Why must the 1-D 0/1 knapsack loop capacity downwards?Related
Answer: So dp[c − w] still holds the value from before this item, and the item is used at most once
Going up, dp[c − w] may already include the current item, which would let it be taken twice.
From Knapsack problems
Coins {1, 3, 4} and amount 6: how many coins do greedy and the best answer use?Related
Merging sorted arrays of sizes n and m with two pointers costs…Related
Answer: O(n + m)
Each step moves one pointer forward, so there are n + m steps in total.
From Two pointers
Merging [1, 10] and then [2, 3]: what goes wrong if you set the end to the new end instead of the max?Related
Answer: The result becomes [1, 3], losing part of [1, 10]
The inner interval ends earlier, so the merged end must stay max(10, 3) = 10.
From Interval problems
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
- This is the last topic of “Heaps and greedy algorithms”. The module exam comes next.
- 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.