The 3 Backtracking Patterns Behind Most Interview Questions

Backtracking builds answers one choice at a time, explores where each choice leads, and undoes it before trying the next one.

Almost every backtracking question is a twist on one of these three patterns. Learn their decision trees and you can adapt them quickly.

1) Subsets

Generate every subset of a list. Each path from the root of the decision tree is a subset, so every node, not just the leaves, is part of the answer. Only choosing elements after the current index stops the same subset from appearing twice.

1. Subsets

Given a list of distinct numbers, return every possible subset.

Every node in the tree is an answer. Each branch only adds numbers to the right of the last choice, so no subset repeats.

def subsets(nums):
    out = []
    def build(start, path):
        out.append(path[:])
        for i in range(start, len(nums)):
            path.append(nums[i])
            build(i + 1, path)
            path.pop()
    build(0, [])
    return out

n numbers give 2ⁿ subsets, including the empty one.

2) Combinations

Choose exactly k items out of n. The tree looks like the subsets tree, but you stop going deeper once the path holds k items, and only those full paths are answers.

2. Combinations

Given numbers 1 to n, return every way to choose k of them.

Same shape as subsets, but a path stops growing at k items, and only those paths are answers.

n = 4 k = 2

[1,2] [1,3] [1,4] [2,3] [2,4] [3,4]

def combine(n, k):
    out = []
    def build(start, path):
        if len(path) == k:
            out.append(path[:])
            return
        for x in range(start, n + 1):
            path.append(x)
            build(x + 1, path)
            path.pop()
    build(1, [])
    return out

3) Permutations

Produce every ordering of a list. Unlike subsets, order matters, so each level may pick any number not used yet. Only the leaves, where every number has been placed, are answers.

3. Permutations

Given distinct numbers, return every possible ordering.

Each level can use any number that is not already in the path. Only the leaves are answers.

[1,2,3] [1,3,2] [2,1,3] [2,3,1] [3,1,2] [3,2,1]

def permute(nums):
    out = []
    def build(path, used):
        if len(path) == len(nums):
            out.append(path[:])
            return
        for i, x in enumerate(nums):
            if not used[i]:
                used[i] = True
                build(path + [x], used)
                used[i] = False
    build([], [False] * len(nums))
    return out