Description

Backtracking is a Depth First Search on the tree of partial solutions. It works well when we have a continue vs dont continue flow. Make a choice, explore where it leads, then UNDO the choice and try the next one — the undo (jumping back to the previous state) is what makes it backtracking. Pruning branches that can't lead to a valid solution ("dont continue") as early as possible is where all the speed comes from.

Use it to enumerate all configurations: permutations, combinations, subsets, N-Queens, Sudoku, word search.

Runtime

Exponential — the solution tree is the cost: O(2^n) for subsets, O(n!) for permutations. Pruning shrinks the tree but not the worst case.

Visualization

Backtrack-1783029449775.webp|611

Pseudocode

answers = []

def backtrack(current_state):
	if solution(current_state):
		answers.append(current_state)
	else:
		for option in options:
			if we want to take option:      # prune here
				take option                 # choose
				backtrack(new_state)        # explore
				undo option                 # un-choose

Code

def permutations(nums):
    res = []
    path = []

    def backtrack():
        if len(path) == len(nums):     # solution
            res.append(path[:])        # record a copy
            return

        for n in nums:                 # each option
            if n in path:              # prune: already used
                continue
            path.append(n)             # choose
            backtrack()                # explore
            path.pop()                 # un-choose

    backtrack()
    return res