2 min read

Backtracking Algorithms

Backtracking is an algorithmic technique for solving problems recursively by trying to build a solution incrementally, one piece at a time, and removing those solutions that fail to satisfy the constraints of the problem at any point in time.

1. The Core Concept

Think of backtracking as exploring a maze. You choose a path and follow it until you hit a dead end. Once you hit a dead end, you turn back ("backtrack") to the last junction and try a different path.

  • Depth-First Search (DFS): Backtracking uses DFS to explore the "state space tree".
  • Pruning: If the current partial solution cannot possibly lead to a valid full solution, we abandon it immediately. This avoids wasting time on invalid paths.

2. General Structure

Most backtracking algorithms follow this template:

def backtrack(candidate):
    # 1. Check if we found a valid solution
    if is_solution(candidate):
        output(candidate)
        return

    # 2. Iterate through all possible next steps
    for next_step in possible_steps:
        if is_valid(next_step):
            # 3. Make a choice (Place)
            place(next_step)

            # 4. Recurse (Explore)
            backtrack(next_step)

            # 5. Undo the choice (Backtrack)
            remove(next_step)

3. Example: Permutations

Given an array [1, 2, 3], return all possible permutations.

def permute(nums):
    result = []

    def backtrack(path):
        # Base Case: If path length equals input length, we have a permutation
        if len(path) == len(nums):
            result.append(path[:]) # Append a copy of the path
            return

        for num in nums:
            # Constraint: Don't use the same number twice
            if num in path:
                continue

            path.append(num) # Choose
            backtrack(path)  # Explore
            path.pop()       # Un-choose (Backtrack)

    backtrack([])
    return result

4. Common Use Cases

Backtracking is ideal for Constraint Satisfaction Problems:

  • Puzzles: Sudoku, Crosswords, N-Queens problem.
  • Combinatorics: Generating permutations, combinations, or subsets.
  • Pathfinding: Finding a path through a maze.

5. Backtracking vs. Dynamic Programming

  • Backtracking: Explores all potential solutions (brute force with pruning). It is typically used when you need all solutions or when the problem doesn't have optimal substructure.
  • Dynamic Programming: Breaks problems into overlapping subproblems and caches results. It is typically used for optimization problems (finding the best solution).

programming/algorithms programming/dynamic-programming programming/functions-and-scope