Algorithm Notes Archive-backed notes · Original guides

Backtracking vs Iterative Generation for Combinatorial Problems

Many interview problems ask you to generate every valid combination, subset, or sequence. The output may look different, but the underlying decision is often the same: should you explore the search space recursively with backtracking, or construct the results iteratively?

Both approaches can be correct. The better choice depends on the shape of the choices, whether branches can be pruned, and how clearly the implementation communicates the state being built.

The Shared Model: A Growing Set of Decisions

Combinatorial generation problems build one result through a sequence of choices. For a phone keypad, each digit contributes one letter. For a power set, each input element is either included or excluded.

Before choosing an implementation, identify:

  • the choices available at each step
  • the state that must be carried forward
  • the condition that makes a result complete
  • whether an invalid branch can be rejected early

Once those pieces are explicit, backtracking and iterative generation become two different ways to traverse the same conceptual search space.

Backtracking

Backtracking uses a recursive function to extend one partial result at a time. It is a depth-first traversal of the decision tree.

search(position, path):
  if position is complete:
    save a copy of path
    return

  for each choice at position:
    add choice to path
    search(next position, path)
    remove choice from path

The remove step is what makes the technique backtracking: after one branch is explored, the mutable path returns to its previous state before the next choice begins.

When Backtracking Is a Strong Fit

Use backtracking when:

  • the number of choices changes from one position to another
  • the problem adds constraints or pruning rules
  • output order follows a natural depth-first traversal
  • the current path is easier to express than a numeric encoding
  • you may later adapt the solution to permutations or constraint search

The Phone Keypad Letter Combinations problem is a good example. Each digit maps to a small group of letters, and the recursion depth matches the number of input digits.

Iterative Expansion

Iterative expansion starts with one empty result and repeatedly adds the choices for the next input item.

results = [empty result]

for each input item:
  next_results = []
  for each partial result in results:
    for each available choice:
      append partial result plus choice to next_results
  results = next_results

This is a breadth-by-breadth view of the same decision tree. After processing one input item, results contains every partial result of the same length.

When Iterative Expansion Is a Strong Fit

Use iterative expansion when:

  • every partial result should be materialized anyway
  • the state transition is uniform at each step
  • avoiding recursion improves clarity in the target language
  • you want to inspect or process results level by level

Its main cost is memory: it keeps the entire current frontier instead of only one active path.

Bit Masks: A Compact Iterative Form

When every element has exactly two choices, include or exclude, a bit mask can encode the decision sequence. For N elements, the integers from 0 through 2^N - 1 represent all possible subsets.

Bit masks are concise and predictable, but they are less flexible when choices have more than two states or when pruning matters. The Power Set Generation guide shows both bit masking and backtracking on the same problem.

Complexity Does Not Disappear

Changing the traversal does not reduce the number of required outputs. If a problem produces K results of length up to N, fully materializing them generally takes O(K * N) time and output space.

The auxiliary-space distinction is more useful:

  • backtracking usually keeps an O(N) active path and recursion stack, excluding output
  • iterative expansion may keep a large frontier of partial results
  • bit masking uses small traversal state, but still allocates every completed subset that must be returned

This is why interview answers should separate auxiliary space from output space.

A Practical Decision Checklist

Choose backtracking if you expect constraints, pruning, permutations, or variable branching. Choose iterative expansion if every level follows the same transformation and holding the frontier is acceptable. Choose bit masks when every item is a clean binary decision and compact code improves rather than hides the explanation.

If two approaches have the same asymptotic cost, prefer the one whose state and correctness are easiest to explain. In an interview, a clear invariant is usually more valuable than saving a few lines of code.

Common Mistakes

  • Saving the same mutable path object instead of copying it.
  • Forgetting to undo a choice after a recursive call.
  • Claiming low space complexity while ignoring the required output.
  • Using bit operations when the input size can exceed the language's safe bit width.
  • Generating every candidate first when a constraint could prune branches early.

Backtracking, iterative expansion, and bit masks are not unrelated tricks. They are traversal strategies for the same family of decision spaces. Recognizing that shared model makes it easier to move from memorized solutions to reusable problem-solving patterns.