Skip to main content
Backtracking Pattern

What is Backtracking?

Backtracking is a systematic way to explore all possible solutions by building candidates incrementally and abandoning a path (“backtracking”) as soon as it violates constraints.
Quick Recognition: If you need to generate all possibilities, find all valid combinations, or solve constraint satisfaction problems, think Backtracking!

Pattern Recognition Checklist

Use Backtracking When

  • Need all combinations/permutations
  • Problem is about constraint satisfaction
  • Generate all valid solutions
  • Need to explore decision tree
  • Pruning can reduce search space

Don't Use When

  • Only need one solution (might use simpler approach)
  • Optimal solution needed (use DP instead)
  • Counting only (DP often better)
  • No way to prune invalid paths

When to Use

Combinations/Permutations

Generate all arrangements or selections

Subsets

Generate all possible subsets

Constraint Satisfaction

Sudoku, N-Queens, crosswords

Path Finding

Find all paths, word search

Core Template

def backtrack(path, choices):
    if is_solution(path):
        result.append(path[:])  # Save a copy
        return
    
    for choice in choices:
        if is_valid(choice, path):
            # Make choice
            path.append(choice)
            
            # Explore with this choice
            backtrack(path, updated_choices)
            
            # Undo choice (backtrack)
            path.pop()

Pattern Variations

1. Subsets (All Combinations)

def subsets(nums):
    """Generate all subsets of nums"""
    result = []
    
    def backtrack(start, current):
        result.append(current[:])  # Every path is valid
        
        for i in range(start, len(nums)):
            current.append(nums[i])
            backtrack(i + 1, current)  # Move forward only
            current.pop()
    
    backtrack(0, [])
    return result

# Example: [1,2,3] -> [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]

2. Permutations

def permutations(nums):
    """Generate all permutations of nums"""
    result = []
    used = [False] * len(nums)
    
    def backtrack(current):
        if len(current) == len(nums):
            result.append(current[:])
            return
        
        for i in range(len(nums)):
            if used[i]:
                continue
            
            used[i] = True
            current.append(nums[i])
            
            backtrack(current)
            
            current.pop()
            used[i] = False
    
    backtrack([])
    return result

3. Combinations (Choose K)

def combinations(n, k):
    """Choose k numbers from 1 to n"""
    result = []
    
    def backtrack(start, current):
        if len(current) == k:
            result.append(current[:])
            return
        
        # Pruning: need k - len(current) more numbers
        remaining_needed = k - len(current)
        for i in range(start, n - remaining_needed + 2):
            current.append(i)
            backtrack(i + 1, current)
            current.pop()
    
    backtrack(1, [])
    return result

4. N-Queens

def solve_n_queens(n):
    """Place n queens on n x n board so no two attack each other"""
    result = []
    board = [['.'] * n for _ in range(n)]
    
    def is_safe(row, col):
        # Check column
        for i in range(row):
            if board[i][col] == 'Q':
                return False
        
        # Check upper-left diagonal
        i, j = row - 1, col - 1
        while i >= 0 and j >= 0:
            if board[i][j] == 'Q':
                return False
            i -= 1
            j -= 1
        
        # Check upper-right diagonal
        i, j = row - 1, col + 1
        while i >= 0 and j < n:
            if board[i][j] == 'Q':
                return False
            i -= 1
            j += 1
        
        return True
    
    def backtrack(row):
        if row == n:
            result.append([''.join(r) for r in board])
            return
        
        for col in range(n):
            if is_safe(row, col):
                board[row][col] = 'Q'
                backtrack(row + 1)
                board[row][col] = '.'
    
    backtrack(0)
    return result

Classic Problems

Pattern: Include or exclude each elementKey: Every path is a valid subset
Pattern: Use boolean array to track used elementsKey: All positions are valid choices
Pattern: Subsets with target sum, elements reusableKey: Start from same index (not i+1) for reuse

Common Mistakes

Avoid These Pitfalls:
  1. Not copying path: Use path[:] or new ArrayList(path) when saving
  2. Forgetting to backtrack: Always undo the choice after exploring
  3. Duplicate results: Skip same values at same level for unique results
  4. Infinite loops: Ensure progress by moving start index forward

The Backtracking Template

Every backtracking solution follows this structure:
function backtrack(path, choices):
    if is_solution(path):
        save(path)
        return
    
    for choice in choices:
        if is_valid(choice):
            make_choice(path, choice)
            backtrack(path, remaining_choices)
            undo_choice(path, choice)  # BACKTRACK!

Problem Type Quick Reference

Problem TypeInclude/ExcludeStart IndexReuse Elements
SubsetsEvery node is resulti + 1No
CombinationsLeaf nodes onlyi + 1No
Combination SumLeaf nodes onlyi (same)Yes
PermutationsLeaf nodes only0 (use visited)No
Unique ResultsSkip duplicates at same leveli + 1No

Interview Problems by Company

ProblemCompanyKey Concept
SubsetsAll FAANGBasic include/exclude
PermutationsAll FAANGUsed array tracking
Combination SumAmazon, MetaReusable elements
Letter CombinationsMetaPhone keypad mapping
Word SearchAmazon, MetaGrid backtracking

Interview Tips

Script for interviews:
  1. “This requires generating all possibilities, so I’ll use backtracking.”
  2. “I’ll build solutions incrementally, making one choice at a time.”
  3. “At each step, I’ll check if the current path is valid.”
  4. “After exploring a choice, I’ll undo it (backtrack) and try the next.”
  5. “I’ll prune invalid paths early to optimize.”
Interviewer SaysYou Should Think
”Generate all combinations”Backtracking
”Find all permutations”Backtracking with used array
”All valid configurations”Backtracking with constraints
”Can you prune?”Early termination conditions
”Handle duplicates”Sort + skip same at same level
When input has duplicates but you need unique results:
  1. Sort the input first
  2. Skip duplicates at same level: if i > start and nums[i] == nums[i-1]: continue
  3. This ensures same value isn’t used twice at same position in recursion tree

Practice Problems

ProblemDifficultyLink
SubsetsMediumLeetCode 78
PermutationsMediumLeetCode 46
Combination SumMediumLeetCode 39
N-QueensHardLeetCode 51
Word SearchMediumLeetCode 79

Practice Roadmap

1

Day 1: Subsets

  • Solve: Subsets, Subsets II
  • Focus: Include/exclude pattern
2

Day 2: Combinations

  • Solve: Combinations, Combination Sum
  • Focus: Target constraints
3

Day 3: Permutations

  • Solve: Permutations, Permutations II
  • Focus: Used array tracking
4

Day 4: Grid Problems

  • Solve: Word Search, N-Queens
  • Focus: 2D backtracking
Interview Tip: Draw the decision tree to visualize choices at each step. This helps identify the base case and pruning conditions.