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.
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:
"""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
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
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
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.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
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
- itertools: combinations() and combinations_with_replacement() (The Python Standard Library) (Python Software Foundation)
- math.comb() (The Python Standard Library) (Python Software Foundation)
- NIST Dictionary of Algorithms and Data Structures: combination (National Institute of Standards and Technology (NIST))
- Array.prototype.sort() (comparing numbers) (MDN Web Docs)
- The Java Language Specification, Java SE 25: 15.18.2 Additive operators (+ and -) for numeric types (Oracle)
- Package slices: Sort, Compact and Clone (The Go Authors)
- UndefinedBehaviorSanitizer (Clang documentation) (The LLVM Project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress