Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 16 – Backtracking

Combinations and combination sums

Choose k of n items with a start index, list every combination sum that reaches a target with or without reuse, and prune the search by sorting.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 8 of 35 minutes free
Free: Two problems about choosing · The brute force, and where it stops · What to notice. With a pass: the other 12 sections and 3 interview answers, about 27 minutes.

What you will learn

  • Generate every combination of k items with a start index, and stop early when too few items are left
  • Find every combination that reaches a target, with values reused or each item used once
  • Avoid repeated combinations when values repeat
  • Measure what sorting, an early stop and the skip rule each save in the search
  • Count the combinations with a table when listing them is too slow

Before you start

What you’ll learn · Before you start

What you will learn

  • Generate every combination of k items with a start index, and stop early when too few items are left
  • Find every combination that reaches a target, with values reused or each item used once
  • Avoid repeated combinations when values repeat
  • Measure what sorting, an early stop and the skip rule each save in the search
  • Count the combinations with a table when listing them is too slow

Before you start

On this page

A combination is a choice of items in which order does not matter: n items have C(n, k) combinations of size k, and 20 people make 184,756 different teams of 10. Combinations are listed by the backtracking search with a start index, which adds items only after the last one chosen, so each set is built once. Combination sums ask for every choice of numbers that adds up to a target. When a value may be used again, the recursive call keeps the same start index; when each item is used at most once, it moves past it. Sorting lets a loop stop at the first value larger than what is left. A skip rule drops repeated values at one depth.

Two problems about choosing

Problem A: teams with a pair. teams(volunteers, k, pair) lists every team of k volunteers that holds both people of pair or neither. Limits: n ≤ 20. With volunteers A to E, k = 3 and the pair A and B, the teams are ABC, ABD, ABE and CDE.

Problem B: an exact fare. These are combination sums over a purse. fare_combinations(coins, fare) lists every distinct way to pay fare exactly, each coin used at most once, where values repeat. Limits: up to 30 coins, values 1 to 100, a fare up to 500.

  • The coins 5, 2, 2, 10, 1, 5, 1 pay 12 in four ways: 1 1 5 5, 1 1 10, 2 5 5 and 2 10.
  • When each value may be used any number of times, 2, 5 and 10 pay 12 as 2 2 2 2 2 2, 2 5 5 and 2 10.

The brute force, and where it stops

Try every subset of the positions, keep those whose coins add up to the fare, and drop repeated combinations with a set:

Every subset of the purse, timed Python · brute/brute_force.py
"""Both problems by brute force: every subset of the positions, filtered, repeats dropped with a set."""
import time
from itertools import combinations


def teams_brute(volunteers, k, pair):
    """Every team of k volunteers that holds both of pair or neither."""
    a, b = pair
    return ["".join(t) for t in combinations(volunteers, k) if (a in t) == (b in t)]


def fare_brute(coins, fare):
    """Every distinct combination of coins, each used at most once, that adds up to fare exactly."""
    found = set()
    for mask in range(1 << len(coins)):  # bit i says whether coins[i] is used
        chosen = [coins[i] for i in range(len(coins)) if mask >> i & 1]
        if sum(chosen) == fare:
            found.add(tuple(sorted(chosen)))
    return [list(c) for c in sorted(found)]


print(teams_brute("ABCDE", 3, ("A", "B")))
print(fare_brute([5, 2, 2, 10, 1, 5, 1], 12))

purse = [1, 2, 5, 10, 20] * 6  # 30 coins: six of each value
print("coins  subsets tried  distinct ways to pay 35  time")
for n in (10, 15, 20):
    start = time.perf_counter()
    ways = fare_brute(purse[:n], 35)
    seconds = time.perf_counter() - start
    print(f"{n:5} {1 << n:>14,} {len(ways):>24}  {seconds:.2f} s")
print(f"30 coins: {1 << 30:,} subsets to try")

Output

['ABC', 'ABD', 'ABE', 'CDE']
[[1, 1, 5, 5], [1, 1, 10], [2, 5, 5], [2, 10]]
coins  subsets tried  distinct ways to pay 35  time
   10          1,024                        4  0.00 s
   15         32,768                       12  0.03 s
   20      1,048,576                       20  4.14 s
30 coins: 1,073,741,824 subsets to try

This output changes from run to run: the times depend on the computer and on what else it is doing

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py

The purse holds six coins each of 1, 2, 5, 10 and 20, and the table uses its first 10, 15 and 20 coins. Every 5 coins more multiply the subsets by 32, so 30 coins cost 1,024 times the work of 20, about half an hour of Python (the constraints lesson gives such budgets), to find a few dozen ways. For teams the brute force is acceptable, C(20, 10) is 184,756, but it still builds teams that break the pair rule before rejecting them.

What to notice

The coins 2, 5 and 5 in three orders, two of them dashed as repeats, beside one increasing order built with a start index.any order: built three times255= 12525again552againa start index: one order255the next coin comes from the sameplace in the sorted list or a laterone, so values never decreasedashed: orders it never builds

Why combinations need a start index

Text description of the diagram

The left panel shows one combination that pays 12, the coins 2, 5 and 5, written in three orders: 2, 5, 5 with a solid outline, then 5, 2, 5 and 5, 5, 2 with dashed outlines and the word "again". A search that may take the coins in any order builds the same combination three times.

The right panel shows the coins 2, 5 and 5 joined by arrows in increasing order. With a start index, each next coin comes from the same place in the sorted list or a later one, so the values never decrease along a path and the combination is built in one order only. A note says that the dashed orders are the ones such a search never builds.

A combination is built once when its values never decrease along a path, and a start index guarantees that. Reuse then means recursing from the same index; using each coin once means recursing from the next one. On sorted coins, the first coin larger than the amount left ends the loop, because every coin after it is at least as large. Two coins of the same value tried at one depth build the same combinations twice.

Choosing k of n, and the bound that stops early Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The pair that must go together, without a search
  • Combination sums with reuse: the same index again
  • Each coin once, with repeated values: the skip rule
    • Why each combination is found exactly once, a proof
  • What sorting and the skip rule save, measured
  • The code in five languages
    • Checked against the brute force on 2,000 purses
  • Counting instead of listing
  • Variants
  • Edge cases and common bugs
  • Exercise: every way to pay a fare from a purse
    • Every way to pay an exact fare from a purse
  • A practice set with answers
  • Check yourself
  • Interview questions
    • Why does a search for combinations pass a start index to each call, and what goes wrong without it?
    • A combination-sum function must support two modes: each value may be used any number of times, or each listed coin at most once, when the list repeats values. What changes between the two, and how do you test that neither mode lists a combination twice or misses one?
    • A combination-sum search over a purse of 30 coins with many repeated values takes far too long. A colleague proposes sorting the coins and breaking out of the loop early. Will that fix it? Say what you would measure, and what actually removes the work.
    • A fare system wants only the number of ways to pay each amount up to 5,000 with unlimited coins of 1, 2, 5, 10 and 20. Why is listing the wrong tool, how do you compute the counts, and what type must hold them?
  • Key takeaways

9 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.