Skip to content

Intermediate: core algorithms · Heaps and greedy algorithms · topic 40 of 61 · 40 min

More greedy classics

Explanation

A handful of greedy problems come up again and again: the jump game (track the farthest reachable index), the gas station circuit (restart after a deficit), boats to save people (pair the heaviest with the lightest), and the fractional knapsack (take the best value per kilo first). Huffman coding builds short codes for frequent symbols by always merging the two rarest. Knowing why each greedy rule works, and why the 0/1 knapsack needs dynamic programming instead, is the real lesson.

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.

python
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 False
In the second list every path lands on index 3, whose value is 0.

Gas 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.

python
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: 3

Boats 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).

python
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 4

Fractional 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).

python
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.0
All of the first two items, then 20 of the 30 kilos of the third: 60 + 100 + 80.

0/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.

python
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}
Code lengths in bits. The common "a" gets 1 bit; the rare "e" and "f" get 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

More greedy classics

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.

python
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:
        break
Shows cardamom 2 kg, dal 30 kg, then rice 8 kg: the costliest per kilo goes in first.

Practice (0/4 done)

Task 1 · Code · Python

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.

Task 2 · Code · Python

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.

Task 3 · Code · Python

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.

Task 4 · Code · Python

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

Question 1

1.In the jump game, what single number do you track while walking left to right?

Question 2

2.Gas station: starting at s, the tank first goes negative when reaching station j + 1. What can you conclude?

Question 3

3.Why does the fractional knapsack greedy fail on the 0/1 knapsack?

Question 4

4.In boats to save people, who should the heaviest person share a boat with?

Question 5

5.In Huffman coding, which two nodes are merged at each step?

Read more (free)

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

Answer: Greedy 3, best 2

Greedy takes 4 + 1 + 1. The best is 3 + 3.

From Greedy algorithms: the idea

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

  1. This is the last topic of “Heaps and greedy algorithms”. The module exam comes next.
  2. Module 8 exam: Heaps and greedy algorithms · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Graphs. 61 topics left in the course before the final exam.

← Previous: Interval problems