Skip to content

What is Backtracking?

Patterns·6 min read·code in python

Backtracking is a general technique for solving problems by building a solution one choice at a time and abandoning a partial solution the moment it cannot lead to a valid answer. It explores a tree of choices depth-first, and when it hits a dead end it undoes the last choice and tries another.

The core idea: choose, explore, undo

Imagine walking a maze while unspooling a thread. At each junction you pick a direction and keep going. When you hit a wall, you follow the thread back to the last junction and try a different direction. Backtracking is that thread-and-junction idea turned into code: make a choice, recurse to explore its consequences, and if that path fails, undo the choice and try the next one.

What makes it more than blind trial and error is that you abandon a path the instant it is provably hopeless, not only when it is fully complete. If placing a queen already puts two queens on the same diagonal, there is no point filling the rest of the board, so you undo and move on. That early abandonment is the whole point.

How it works: the state-space tree and pruning

Formally, backtracking performs a depth-first search over a state-space tree. Each node is a partial solution and each edge is one additional choice: the next number in a permutation, the next queen's column, the next digit in a Sudoku cell. A recursive function tries every valid choice at the current level, and after each recursive call it restores state so the next choice starts clean. When the partial solution is complete you record it; when no choice is valid the call simply returns and control flows back up the tree.

Two moves define every backtracking function: the constraint check (is this choice still valid?) and the undo (remove the choice before trying the next). The constraint check is where pruning happens, and the tighter and earlier it is, the more of the tree you skip. This is exactly the kind of process that clicks once you watch it animate: you can see the recursion dive down a branch, hit a violated constraint, and retreat one level to try the next option.

One template, many problems

Almost every backtracking solution fits the same shape: a base case that records a completed solution, a loop over candidate choices, and a choose / recurse / undo trio inside that loop. Learn that skeleton once and permutations, subsets, combination sum, N-Queens, word search, and Sudoku all become variations on where you get the candidates and how you check validity.

The differences are mostly in candidate generation. For subsets you decide include-or-skip on each element; for permutations you pick any unused element; for grid problems you branch into neighboring cells. The recursion, the base case, and the undo stay recognizably the same.

The code

pythonGenerate every permutation of a list by choosing, recursing, then undoing each pick.

def permutations(nums):
    result = []

    def backtrack(path, remaining):
        if not remaining:               # complete solution
            result.append(path[:])      # store a COPY, not the shared list
            return
        for i in range(len(remaining)):
            path.append(remaining[i])                        # choose
            backtrack(path, remaining[:i] + remaining[i + 1:])  # explore
            path.pop()                                       # undo

    backtrack([], nums)
    return result

Run it in the browser IDE, free on the platform. Open the IDE

Complexity

TimeO(n · n!) for permutations; exponential in general
SpaceO(n) recursion depth, plus the space to store results

Backtracking's cost is the number of nodes it visits in the state-space tree, so it is problem-dependent: subsets are O(2^n), N-Queens is roughly O(n!). Strong pruning (constraint checks that fail early) massively cuts the real work but does not change the Big-O worst case.

When to use it

  • Enumerating all permutations, combinations, or subsets of a set
  • Constraint-satisfaction puzzles like N-Queens, Sudoku, or crossword filling
  • Grid search where you explore a path and then retreat, such as word search
  • Partitioning and combination-sum style problems that need every valid arrangement

Watch out for

  • Forgetting to undo the choice (not popping or restoring state), so state leaks between branches and results become wrong
  • Storing a reference to the mutable path instead of a copy, so every recorded result points to the same list that ends up empty
  • Weak or missing pruning and no visited/index guard, which explores the full tree, revisits states, and times out on larger inputs

Backtracking: questions we get

Is backtracking hard to learn?

Not once the pattern clicks. The single sticking point for most people is remembering to undo each choice before trying the next. If you learn one template, choose, recurse, undo, and practice five or six classic problems, backtracking stops feeling mysterious. Watching the recursion step through a decision tree makes it much faster to internalize.

What is the time complexity of backtracking?

It depends on the problem and is usually exponential in the worst case, because you may explore a large tree of choices. Permutations are O(n·n!), subsets are O(2^n), and N-Queens is roughly O(n!). Pruning invalid branches early can dramatically reduce real running time, but it does not change the Big-O upper bound.

What is the difference between backtracking and DFS?

Backtracking is depth-first search applied to a tree of partial solutions, with one addition: you explicitly undo each choice when a branch fails so the shared state stays correct. Plain DFS often just visits nodes; backtracking builds and unbuilds a candidate solution as it goes. Every backtracking algorithm is a DFS, but not every DFS bothers to undo.

When should I use backtracking instead of dynamic programming?

Use backtracking when you need to enumerate or find actual arrangements, all permutations, every valid board, a path that satisfies constraints. Use dynamic programming when you only need an optimal value or a count and the problem has overlapping subproblems you can cache. If you keep recomputing the same states, DP or memoization is probably the better fit.

Related concepts

Reading is the easy part. In a batch you build it, say it out loud in a scored mock round and hear what an interviewer would think.

Talk to mentor