ā†©ļø Backtracking

šŸ”“ Advanced Advanced 75 min read

šŸŽÆ What is Backtracking?

Backtracking is a systematic way to explore all possible solutions by building candidates incrementally and abandoning ("backtracking") a candidate as soon as it's determined it cannot lead to a valid solution. It's like DFS with pruning.
// Generic Backtracking Template
function backtrack(state, choices, result):
    if satisfied(state):
        result.add(copy(state))
        return

    for each choice in choices:
        if isValid(state, choice):
            apply(state, choice)
            backtrack(state, choices, result)
            undo(state, choice)  // ← THE "BACKTRACK" STEP!

ā™› Classic Example: N-Queens

function solveNQueens(int $n): array {
    $result = [];
    $board = array_fill(0, $n, array_fill(0, $n, '.'));
    backtrack($board, 0, $n, $result);
    return $result;
}

function backtrack(array &$board, int $row, int $n, array &$result): void {
    if ($row === $n) {
        $result[] = array_map(fn($r) => implode('', $r), $board);
        return;
    }

    for ($col = 0; $col < $n; $col++) {
        if (isSafe($board, $row, $col, $n)) {
            $board[$row][$col] = 'Q';            // Place queen
            backtrack($board, $row + 1, $n, $result);
            $board[$row][$col] = '.';            // ← BACKTRACK!
        }
    }
}

function isSafe(array $board, int $row, int $col, int $n): bool {
    // Check column above
    for ($i = 0; $i < $row; $i++)
        if ($board[$i][$col] === 'Q') return false;
    // Check upper-left diagonal
    for ($i=$row-1,$j=$col-1; $i>=0&&$j>=0; $i--,$j--)
        if ($board[$i][$j] === 'Q') return false;
    // Check upper-right diagonal
    for ($i=$row-1,$j=$col+1; $i>=0&&$j<$n; $i--,$j++)
        if ($board[$i][$j] === 'Q') return false;
    return true;
}

print_r(solveNQueens(4));
// 2 solutions for n=4, 92 for n=8

šŸ”¢ Generate All Subsets

function subsets(array $nums): array {
    $result = []; $current = [];
    backtrackSubsets($nums, 0, $current, $result);
    return $result;
}

function backtrackSubsets(array $nums, int $start, array &$current, array &$result): void {
    $result[] = $current; // Add current subset

    for ($i = $start; $i < count($nums); $i++) {
        $current[] = $nums[$i];        // Include nums[i]
        backtrackSubsets($nums, $i + 1, $current, $result);
        array_pop($current);           // ← BACKTRACK!
    }
}

print_r(subsets([1,2,3]));
// [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]

šŸ”„ Generate Permutations

function permute(array $nums): array {
    $result = []; $used = array_fill(0, count($nums), false);
    backtrackPermute($nums, [], $used, $result);
    return $result;
}

function backtrackPermute(array $nums, array $current, array &$used, array &$result): void {
    if (count($current) === count($nums)) {
        $result[] = $current; return;
    }
    for ($i = 0; $i < count($nums); $i++) {
        if ($used[$i]) continue;
        $used[$i] = true;
        $current[] = $nums[$i];
        backtrackPermute($nums, $current, $used, $result);
        array_pop($current);
        $used[$i] = false;             // ← BACKTRACK!
    }
}

⚔ Optimization: Pruning

Pruning means cutting off branches that can't lead to a solution early. For N-Queens, we check isSafe() before placing. For Sudoku, we check validity. For Subset Sum, we prune if current sum exceeds target. Pruning transforms brute force into practical backtracking.

šŸ“Š Complexity

N-Queens O(N!) Practically much less with pruning
Subsets O(2ⁿ) All possible subsets
Permutations O(n!) All orderings
Sudoku Solver O(9^(n²)) Vastly reduced by constraints

šŸŽÆ Classic Backtracking Problems

  • N-Queens - Place N queens safely
  • Sudoku Solver - Fill 9Ɨ9 grid
  • Rat in a Maze - Find path through maze
  • Word Search - Find words in 2D grid
  • Combination Sum - Find all combinations summing to target
  • Palindrome Partitioning - Split string into palindromes

šŸ“ Quick Quiz

Q1: The key "backtracking" step involves:

Q2: N-Queens for n=8 has how many solutions?