Recursion & Backtracking Master Recap: Interview Cheatsheet
Advertisement
Recursion & Backtracking Master Recap
Universal Template
def backtrack(state, choices):
# Base case
if is_complete(state):
record_solution(state)
return
# Pruning
if should_prune(state): return
# Explore choices
for choice in get_choices(choices):
if is_valid(state, choice):
make_choice(state, choice) # CHOOSE
backtrack(state, choices) # EXPLORE
undo_choice(state, choice) # UNCHOOSE
Pattern Recognition Cheat Sheet
| Cue | Pattern | Template |
|---|---|---|
| "generate all..." | Subsets/Perms | bt(start, current) |
| "find all valid..." | Constraint satisfaction | bt(row/idx, state) |
| "partition into..." | Splitting | bt(start, parts) |
| "place N pieces..." | N-Queens style | bt(row, conflict_sets) |
| "fill a grid..." | Sudoku style | bt(cell_idx, constraints) |
| "count arrangements" | Bitmask DP or bt | dp[mask] |
| "min removals..." | BFS level-by-level | BFS + validity check |
Duplicate Handling Rules
| Situation | Rule |
|---|---|
| Subsets with dups | Sort + if i > start and nums[i] == nums[i-1]: continue |
| Permutations with dups | Sort + if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continue |
| Combination Sum (no dups) | Start next from i (reuse) or i+1 (no reuse) |
| Buckets/groups | Track seen set for bucket values at same level |
Optimization Checklist
Before coding, ask:
- Can I sort to enable early termination?
- Can I precompute valid choices? (N-Queens sets, palindrome table)
- Is there a DP that replaces backtracking? (subset sum, coin change)
- Can bitmask DP compress state? (arrangements, TSP)
- Should I prune by remaining capacity/length?
Template Library
# Subsets
def subsets(nums):
res = []
def bt(i, cur):
res.append(cur[:])
for j in range(i, len(nums)):
cur.append(nums[j]); bt(j+1, cur); cur.pop()
bt(0, [])
return res
# Permutations
def permute(nums):
res, used = [], [False]*len(nums)
def bt(cur):
if len(cur)==len(nums): res.append(cur[:]); return
for i,n in enumerate(nums):
if used[i]: continue
used[i]=True; cur.append(n); bt(cur); cur.pop(); used[i]=False
bt([]); return res
# Combination Sum
def combinationSum(cands, target):
cands.sort(); res = []
def bt(i, cur, rem):
if rem==0: res.append(cur[:]); return
for j in range(i, len(cands)):
if cands[j]>rem: break
cur.append(cands[j]); bt(j, cur, rem-cands[j]); cur.pop()
bt(0, [], target); return res
# N-Queens
def nQueens(n):
res, cols, d1, d2 = [], set(), set(), set()
board = [['.']*n for _ in range(n)]
def bt(r):
if r==n: res.append([''.join(row) for row in board]); return
for c in range(n):
if c in cols or r-c in d1 or r+c in d2: continue
board[r][c]='Q'; cols.add(c); d1.add(r-c); d2.add(r+c)
bt(r+1)
board[r][c]='.'; cols.discard(c); d1.discard(r-c); d2.discard(r+c)
bt(0); return res
Top 30 Backtracking LeetCode Problems
| # | Problem | Pattern |
|---|---|---|
| 17 | Letter Combinations | Recursive tree |
| 22 | Generate Parentheses | Count tracking |
| 37 | Sudoku Solver | Cell-by-cell constraint |
| 39 | Combination Sum | Reuse + sort + prune |
| 40 | Combination Sum II | No reuse + dup skip |
| 46 | Permutations | Used array |
| 47 | Permutations II | Used + dup skip |
| 51 | N-Queens | Row + set constraints |
| 52 | N-Queens II | Count only, bitmask |
| 77 | Combinations | nCr, pruning |
| 78 | Subsets | Include/exclude |
| 79 | Word Search | Grid DFS + unvisit |
| 90 | Subsets II | Sort + dup skip |
| 93 | Restore IP Addresses | Segment validation |
| 131 | Palindrome Partitioning | Precompute + bt |
| 140 | Word Break II | Memo + bt |
| 212 | Word Search II | Trie + grid DFS |
| 216 | Combination Sum III | k-choose from 1-9 |
| 282 | Expression Add Operators | Operator insertion |
| 301 | Remove Invalid Parentheses | BFS min + DFS all |
| 306 | Additive Number | Partition into additive |
| 320 | Generalized Abbreviation | Include/exclude chars |
| 351 | Android Unlock Patterns | Grid DFS with skip |
| 401 | Binary Watch | Popcount combinations |
| 416 | Partition Equal Subset | DP (not bt) |
| 425 | Word Squares | Trie-guided bt |
| 473 | Matchsticks to Square | K-bucket partition |
| 526 | Beautiful Arrangement | Position-number validity |
| 698 | Partition K Equal Subsets | Bucket dedup |
| 784 | Letter Case Permutation | Toggle case tree |
Complexity Quick Reference
| Problem | Time | Note |
|---|---|---|
| Subsets n elements | O(2^n * n) | Copy cost |
| Permutations n | O(n! * n) | Copy cost |
| Combinations C(n,k) | O(C(n,k) * k) | |
| N-Queens n×n | O(n!) | With pruning |
| Sudoku | O(9^81) worst | Fast with constraints |
| Word Search L on m×n | O(mn4^L) | |
| Combination Sum target T | O(T^(T/min)) | With pruning |
Common Mistakes
- Forgetting to copy current state when recording solution
- Not restoring state after recursive call (missing undo)
- Wrong start index: reuse =
bt(i,...), no-reuse =bt(i+1,...) - Duplicate handling: must sort first, then check
nums[i]==nums[i-1] - Using list in set: use
tuple(current)to add to set - Off-by-one in pruning: check boundary conditions carefully
Advertisement