TechLead
Advanced
28 min
Full Guide

Backtracking

Backtracking technique for solving constraint satisfaction problems

Backtracking is a systematic method for exploring all possible solutions by building candidates incrementally and abandoning each partial candidate (backtracking) as soon as it is determined that it cannot lead to a valid solution. It is the foundation for solving constraint satisfaction problems.

The General Pattern

function backtrack(state, choices) {
  if (isSolution(state)) {
    recordSolution(state);
    return;
  }
  for (const choice of choices) {
    if (isValid(state, choice)) {
      makeChoice(state, choice);          // explore
      backtrack(state, nextChoices(...)); // recurse
      undoChoice(state, choice);          // backtrack
    }
  }
}

Generating All Permutations

Backtracking naturally enumerates all orderings of a set of elements.

function permutations(nums) {
  const result = [];

  function backtrack(current, remaining) {
    if (remaining.length === 0) {
      result.push([...current]);
      return;
    }
    for (let i = 0; i < remaining.length; i++) {
      current.push(remaining[i]);
      backtrack(current, remaining.filter((_, j) => j !== i));
      current.pop();                     // undo
    }
  }

  backtrack([], nums);
  return result;
}

permutations([1, 2, 3]).length; // 6

Combination Sum

Find all combinations of numbers that sum to a target. Numbers can be reused.

function combinationSum(candidates, target) {
  const result = [];

  function backtrack(start, current, remaining) {
    if (remaining === 0) { result.push([...current]); return; }
    for (let i = start; i < candidates.length; i++) {
      if (candidates[i] > remaining) break; // prune — sort candidates first
      current.push(candidates[i]);
      backtrack(i, current, remaining - candidates[i]);
      current.pop();
    }
  }

  candidates.sort((a, b) => a - b);
  backtrack(0, [], target);
  return result;
}

N-Queens

Place N queens on an N×N board so none attack each other. Classic constraint satisfaction problem.

function solveNQueens(n) {
  const cols = new Set(), diag1 = new Set(), diag2 = new Set();
  const board = Array.from({ length: n }, () => Array(n).fill('.'));
  const result = [];

  function backtrack(row) {
    if (row === n) { result.push(board.map(r => r.join(''))); return; }
    for (let col = 0; col < n; col++) {
      if (cols.has(col) || diag1.has(row - col) || diag2.has(row + col)) continue;
      board[row][col] = 'Q';
      cols.add(col); diag1.add(row - col); diag2.add(row + col);
      backtrack(row + 1);
      board[row][col] = '.';
      cols.delete(col); diag1.delete(row - col); diag2.delete(row + col);
    }
  }

  backtrack(0);
  return result;
}

Pruning — The Key Optimisation

Backtracking without pruning is just brute force. The performance gain comes from detecting dead-ends early and cutting entire branches of the search tree. Always ask: what constraint can I check before recursing?

Time Complexity

Backtracking is worst-case exponential (exploring all branches), but effective pruning can reduce the actual runtime by orders of magnitude. For interview problems, state the worst case and explain your pruning strategy.

Continue Learning