Skip to content
ΣDSA Patterns
Menu
Language

Pattern #15

Backtracking

Essential

Choose, explore, undo. Build all valid combinations or search with pruning.

When to use

You must generate all solutions (subsets, permutations, combinations) or search a decision tree with constraints.

Recognition cues

  • Subsets / permutations / combinations
  • Word search / N-Queens style constraints
  • Path building with undo

Common pitfalls

  • Forgetting to undo (mutate path without pop)
  • Wrong start index causing duplicates or misses
  • Not pruning when the partial path is already invalid

90-second recognition drill

Which pattern fits best?

  • Subsets / permutations / combinations
  • Word search / N-Queens style constraints
  • Path building with undo

Interactive

Mental model

A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.

Step 1 of 8
1
2
3

path = []

Permutations of [1,2,3]: choose, explore, undo.

How to think about it

At each step, try a choice, recurse, then undo. The call stack is the current path. Prune when the partial solution cannot lead to a valid complete one. Order choices carefully to avoid duplicate outputs.

Template shapes

Shape Core move Notes
Subsets Include or skip each element Push path copy at every node
Permutations Swap or used[] mask Length == n → record
Combinations Start index i Avoid reordering duplicates

Complexity baseline

Output-sensitive: often O(n·#solutions). Extra space O(n) recursion depth.

From template to problem

  1. Define the state (path, index, remaining target, used mask).
  2. Base case: record a complete valid path.
  3. Loop choices; push; recurse; pop.
  4. Add pruning (sum too large, cell visited, etc.).

Template

Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.

Backtracking · Template
/** Backtracking template: subsets (choose / skip via start index). */
export function subsets(nums: number[]): number[][] {
  const res: number[][] = [];
  const path: number[] = [];
  const dfs = (start: number) => {
    res.push([...path]);
    for (let i = start; i < nums.length; i++) {
      path.push(nums[i]!);
      dfs(i + 1);
      path.pop();
    }
  };
  dfs(0);
  return res;
}
/** Backtracking template: subsets (choose / skip via start index). */
export function subsets(nums: number[]): number[][] {
  const res: number[][] = [];
  const path: number[] = [];
  const dfs = (start: number) => {
    res.push([...path]);
    for (let i = start; i < nums.length; i++) {
      path.push(nums[i]!);
      dfs(i + 1);
      path.pop();
    }
  };
  dfs(0);
  return res;
}
#StatusProblemTypeDone
  1. 1#39 Combination SumGuide
  2. 2#46 PermutationsGuide
  3. 3#78 SubsetsGuide
  4. 4#79 Word SearchGuide
  5. 5#90 Subsets IIGuide
  6. 6#131 Palindrome PartitioningGuide