Overview
A combination is a selection of k items where order does not matter: {1, 2} and {2, 1} are the same combination. There are C(n, k) = n! / (k! (n - k)!) of them. Backtracking generates combinations by always picking the next element from a start index onward, which guarantees each selection appears in exactly one order.
Combination problems usually add constraints: a target sum, a fixed size, allowed reuse, or restricted digits. The start index controls whether items can be reused (recurse with i) or not (recurse with i + 1).
Choosing mushrooms and olives is the same pizza as choosing olives and mushrooms. To avoid listing both, you always pick toppings in menu order: after picking olives, you only consider toppings listed after olives.
When to use it
- Choose k items out of n, order irrelevant.
- Find all groups that reach a target sum.
- Generate teams, committees, or feature subsets of a fixed size.
- Count combinations with Pascal's triangle or the formula.
Problem patterns it solves
Recognize it when: all groups of a fixed size.
- 77. Combinations
- 1286. Iterator for Combination
Recognize it when: groups that sum to a target, with or without reuse.
- 39. Combination Sum
- 40. Combination Sum II
- 216. Combination Sum III
Recognize it when: number of ways without listing them; Pascal's triangle.
- 118. Pascal's Triangle
- 62. Unique Paths (C(m + n - 2, m - 1))
- 377. Combination Sum IV (counts orderings)
Recognize it when: one choice per group (phone keypad).
- 17. Letter Combinations of a Phone Number
Where it is used in real software
Odds of lottery tickets and poker hands are computed with C(n, k).
Selecting reviewers, committees, or test groups of a fixed size.
Machine-learning pipelines evaluate combinations of k features to find the best model inputs.
Key terms
- C(n, k)
- Number of ways to choose k items from n: n! / (k! (n - k)!).
- Start index
- The first index allowed at the current level, preventing reordered duplicates.
- Pascal's rule
- C(n, k) = C(n - 1, k - 1) + C(n - 1, k).
- Pruning by remaining slots
- Stop if fewer elements remain than slots still needed.
How it works, step by step
- 1Recurse with a start index
backtrack(start, path).
- 2Record when full
If path.length === k (or remaining target is 0), save a copy.
- 3Loop from start
Try each i >= start as the next chosen element.
- 4Prune
If n - i < k - path.length, not enough elements remain: stop.
- 5Recurse with i + 1
Use i instead if reuse is allowed.
C(4, 2): combinations of [1, 2, 3, 4] of size 2
start index ensures increasing order inside each combination
| First pick | Second pick options | Results |
|---|---|---|
| 1 | 2, 3, 4 | [1,2], [1,3], [1,4] |
| 2 | 3, 4 | [2,3], [2,4] |
| 3 | 4 | [3,4] |
| 4 | none (pruned) | - |
NOWFirst pick: 1 | Second pick options: 2, 3, 4 | Results: [1,2], [1,3], [1,4]
6 combinations, matching C(4, 2) = 4! / (2! x 2!) = 6. Starting at 4 is pruned because no elements remain for the second slot.
Implementation
function combine(n, k) { const result = [], path = []; function backtrack(start) { if (path.length === k) { result.push([...path]); return; } const need = k - path.length; for (let i = start; i <= n - need + 1; i++) { // prune: leave room for the rest path.push(i); backtrack(i + 1); path.pop(); } } backtrack(1); return result;} // 216. Combination Sum III: k numbers from 1..9 summing to nfunction combinationSum3(k, n) { const result = [], path = []; function backtrack(start, remaining) { if (path.length === k) { if (remaining === 0) result.push([...path]); return; } for (let d = start; d <= 9 && d <= remaining; d++) { path.push(d); backtrack(d + 1, remaining - d); path.pop(); } } backtrack(1, n); return result;} // C(n, k) without overflow for moderate valuesfunction nCk(n, k) { k = Math.min(k, n - k); let result = 1; for (let i = 1; i <= k; i++) result = (result * (n - k + i)) / i; // stays an integer return result;}Complexity and performance
Each result copied in O(k).
C(20, 10) = 184,756.
All C(i, j) for i <= n.
Trade-offs
If order matters, use permutations (n!/(n-k)! results); if not, combinations (fewer by a factor of k!).
Count with DP or the formula; only list when the problem requires the actual groups.
Variants and related techniques
Despite the name it counts ordered sequences, which is a DP over the target, not backtracking.
Precompute factorials and modular inverses to answer C(n, k) mod p in O(1).
Common mistakes
- Restarting the loop at 0.
Fix: That produces permutations of the same combination; always loop from start.
- Computing n! directly.
Fix: Factorials overflow quickly; use the multiplicative formula or Pascal's triangle.
Interview questions
How does the start index prevent duplicates?
Each combination is generated only in increasing index order. Since {2, 1} would require picking index 2 before index 1, it is never produced.
What pruning can you add to combine(n, k)?
Stop the loop when the remaining elements are fewer than the slots still needed: i <= n - (k - path.length) + 1.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 77. Combinations | Medium | Start index and pruning. |
| 216. Combination Sum III | Medium | Size and sum constraints. |
| 39. Combination Sum | Medium | Reuse allowed. |
| 118. Pascal's Triangle | Easy | Pascal's rule. |
| 17. Letter Combinations of a Phone Number | Medium | One choice per group. |