Chapter 20: Backtracking, Constraint Search and State Space
20.01Intermediate6 topics16 min5 interactive animations

Backtracking method

The backtracking method: searching a decision tree by choosing, exploring and unchoosing, maintaining a partial solution, undoing changes exactly, pruning and validity checks.

Learning goals

  • Model a search problem as a decision tree
  • Write the choose-explore-unchoose template and restore state exactly
  • Prune a branch as early as a rule allows
  • Separate validity checks from the search itself

Prerequisites

Recursion and divide-and-conquer fundamentalsStacks and the call stack

Backtracking is how you search a maze with a ball of string: walk down a corridor, and when it is a dead end, rewind to the last junction and try the next corridor.

20.01.1

Decision tree

Many problems ask for every solution, or for one that satisfies rules: all subsets, all arrangements, a placement that violates no constraint. Such a problem can be drawn as a **decision tree**. The root is the empty solution. Each level is one decision (include this value or not, which letter comes next, which column holds this queen) and each edge is one possible answer to it. A leaf, or any node that satisfies the goal, is a solution.

Backtracking is a depth-first walk over that tree. The tree is never stored: it exists implicitly in the recursion, and the call stack holds the path from the root to the current node. Run the animation to watch the tree for the three subsets of {1, 2, 3} grow: every node is a subset, and the 8 nodes are the 8 subsets.

Subsets (power set) by backtracking

Interactive · edit the input

Your input

Try

partial solution

[]

solutions found (0)

none yet
Setup

Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets.

Step 1 / 32
Java
1static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> out) {
2 out.add(new ArrayList<>(path)); // every partial solution is a subset
3 for (int i = start; i < a.length; i++) {
4 path.add(a[i]); // choose
5 subsets(a, i + 1, path, out); // explore
6 path.remove(path.size() - 1); // unchoose
7 }
8}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 32 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets.
2L1depth=1 solutions=0Explore [].
3L2depth=1 solutions=1Record []: every partial solution counts here.
4L4depth=1 solutions=1Choose 1.
5L1depth=2 solutions=1Explore [1].
6L2depth=2 solutions=2Record [1]: every partial solution counts here.
7L4depth=2 solutions=2Choose 2.
8L1depth=3 solutions=2Explore [1, 2].
9L2depth=3 solutions=3Record [1, 2]: every partial solution counts here.
10L4depth=3 solutions=3Choose 3.
11L1depth=4 solutions=3Explore [1, 2, 3].
12L2depth=4 solutions=4Record [1, 2, 3]: every partial solution counts here.
13L6depth=3 solutions=4Unchoose 3: back to [1, 2].
14L6depth=2 solutions=4Unchoose 2: back to [1].
15L4depth=2 solutions=4Choose 3.
16L1depth=3 solutions=4Explore [1, 3].
17L2depth=3 solutions=5Record [1, 3]: every partial solution counts here.
18L6depth=2 solutions=5Unchoose 3: back to [1].
19L6depth=1 solutions=5Unchoose 1: back to [].
20L4depth=1 solutions=5Choose 2.
21L1depth=2 solutions=5Explore [2].
22L2depth=2 solutions=6Record [2]: every partial solution counts here.
23L4depth=2 solutions=6Choose 3.
24L1depth=3 solutions=6Explore [2, 3].
25L2depth=3 solutions=7Record [2, 3]: every partial solution counts here.
26L6depth=2 solutions=7Unchoose 3: back to [2].
27L6depth=1 solutions=7Unchoose 2: back to [].
28L4depth=1 solutions=7Choose 3.
29L1depth=2 solutions=7Explore [3].
30L2depth=2 solutions=8Record [3]: every partial solution counts here.
31L6depth=1 solutions=8Unchoose 3: back to [].
32L8solutions=8 nodes=8 return=[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]Search finished: 8 solutions, 7 choices tried. Backtracking explores every branch the pruning rules allow.
20.01.2

Choose-explore-unchoose

Every backtracking routine has the same three-beat rhythm inside its loop: **choose** one option (add it to the partial solution), **explore** everything that can follow it (a recursive call), and **unchoose** it (remove it again), so the next option starts from exactly the state the loop started with. The two Java rules that make it work: add a copy of the path to the results, and undo with the exact inverse of the choice.

Step through the “Three values” preset and watch the message log: “Choose 2”, the deeper calls, then “Unchoose 2: back to [1]”. The partial solution in the first auxiliary row always returns to its earlier value after the unchoose.

Java
1interface Search<O> {
2 boolean complete(); // is the partial solution a full answer?
3 List<O> options(); // the choices available right now
4 void apply(O option); // choose
5 void undo(O option); // unchoose: the exact inverse of apply
6 String snapshot(); // a copy of the current partial solution
7}
8static <O> void solve(Search<O> s, List<String> out) {
9 if (s.complete()) { out.add(s.snapshot()); return; }
10 for (O o : s.options()) {
11 s.apply(o); // choose
12 solve(s, out); // explore
13 s.undo(o); // unchoose: restore the state exactly
14 }
15}

Subsets (power set) by backtracking

Interactive · edit the input

Your input

Try

partial solution

[]

solutions found (0)

none yet
Setup

Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets.

Step 1 / 32
Java
1static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> out) {
2 out.add(new ArrayList<>(path)); // every partial solution is a subset
3 for (int i = start; i < a.length; i++) {
4 path.add(a[i]); // choose
5 subsets(a, i + 1, path, out); // explore
6 path.remove(path.size() - 1); // unchoose
7 }
8}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 32 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Each value is either in the subset or not. Backtracking builds the subsets one value at a time: choose a value, explore everything after it, then unchoose. 3 values give 2^3 = 8 subsets.
2L1depth=1 solutions=0Explore [].
3L2depth=1 solutions=1Record []: every partial solution counts here.
4L4depth=1 solutions=1Choose 1.
5L1depth=2 solutions=1Explore [1].
6L2depth=2 solutions=2Record [1]: every partial solution counts here.
7L4depth=2 solutions=2Choose 2.
8L1depth=3 solutions=2Explore [1, 2].
9L2depth=3 solutions=3Record [1, 2]: every partial solution counts here.
10L4depth=3 solutions=3Choose 3.
11L1depth=4 solutions=3Explore [1, 2, 3].
12L2depth=4 solutions=4Record [1, 2, 3]: every partial solution counts here.
13L6depth=3 solutions=4Unchoose 3: back to [1, 2].
14L6depth=2 solutions=4Unchoose 2: back to [1].
15L4depth=2 solutions=4Choose 3.
16L1depth=3 solutions=4Explore [1, 3].
17L2depth=3 solutions=5Record [1, 3]: every partial solution counts here.
18L6depth=2 solutions=5Unchoose 3: back to [1].
19L6depth=1 solutions=5Unchoose 1: back to [].
20L4depth=1 solutions=5Choose 2.
21L1depth=2 solutions=5Explore [2].
22L2depth=2 solutions=6Record [2]: every partial solution counts here.
23L4depth=2 solutions=6Choose 3.
24L1depth=3 solutions=6Explore [2, 3].
25L2depth=3 solutions=7Record [2, 3]: every partial solution counts here.
26L6depth=2 solutions=7Unchoose 3: back to [2].
27L6depth=1 solutions=7Unchoose 2: back to [].
28L4depth=1 solutions=7Choose 3.
29L1depth=2 solutions=7Explore [3].
30L2depth=2 solutions=8Record [3]: every partial solution counts here.
31L6depth=1 solutions=8Unchoose 3: back to [].
32L8solutions=8 nodes=8 return=[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]Search finished: 8 solutions, 7 choices tried. Backtracking explores every branch the pruning rules allow.
20.01.3

Partial solution

The **partial solution** is the decisions made on the way from the root to the current node. It can be a list (the chosen values so far), a set of occupied squares, or an assignment of colours to the first few vertices. It is the state that choose and unchoose modify, and it is what a solution is copied from when the search reaches a complete answer.

Keep the partial solution *small* and *incrementally updatable*. Alongside it, keep whatever lets you test a new option in O(1): a boolean[] used, a column/diagonal marker array for queens, running sums. The animation's first auxiliary row shows the partial solution at every step; the final result lists every complete solution that was copied out of it.

Java
1static List<List<Integer>> combinations(int[] a, int k) {
2 List<List<Integer>> out = new ArrayList<>();
3 build(a, k, 0, new ArrayList<>(), out);
4 return out;
5}
6private static void build(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out) {
7 if (path.size() == k) { out.add(new ArrayList<>(path)); return; } // copy the partial solution
8 for (int i = start; i < a.length; i++) {
9 path.add(a[i]); // choose
10 build(a, k, i + 1, path, out); // explore
11 path.remove(path.size() - 1); // unchoose
12 }
13}
20.01.4

Undo changes

Unchoose must reverse *everything* the choice changed. The bug that hides longest is a change that is not undone: a used[i] = true without the matching used[i] = false, a counter incremented but never decremented, a cell marked visited and never freed. The search then silently skips valid branches, so it finds fewer solutions than exist, with no exception to warn you.

Two patterns make undoing safe. **Mutate and restore**: change the shared state, recurse, change it back (as permute does with used[]). **Pass copies**: give each recursive call its own new value (String s + c, an immutable list), so there is nothing to undo, at the cost of allocating. The permutation animation shows the mutate-and-restore pattern: look at which indices are marked used on the way down and freed on the way back.

Java
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out) {
2 if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }
3 for (int i = 0; i < a.length; i++) {
4 if (used[i]) continue; // already on the path
5 used[i] = true; path.add(a[i]); // choose: two pieces of state change
6 permute(a, used, path, out);
7 path.remove(path.size() - 1); used[i] = false; // unchoose: both are restored
8 }
9}

Permutations, including unique permutations with duplicates

Interactive · edit the input

Your input

Try

partial solution

[]

solutions found (0)

none yet
Setup

at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings.

Step 1 / 69
Java
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out, boolean unique) { // sort a first when unique
2 if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }
3 for (int i = 0; i < a.length; i++) {
4 if (used[i]) continue; // already on the path
5 if (unique && i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue; // a twin of the previous value, which is free
6 used[i] = true; path.add(a[i]); // choose
7 permute(a, used, path, out, unique); // explore
8 path.remove(path.size() - 1); used[i] = false; // unchoose
9 }
10}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 69 steps
StepLineStateWhat happens
1L1depth=0 solutions=0at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings.
2L1depth=1 solutions=0Explore [].
3L6depth=1 solutions=0Choose 1.
4L1depth=2 solutions=0Explore [1].
5L4depth=3 solutions=0Reject 1: already on the path.
6L6depth=2 solutions=0Choose 2.
7L1depth=3 solutions=0Explore [1, 2].
8L4depth=4 solutions=0Reject 1: already on the path.
9L4depth=4 solutions=0Reject 2: already on the path.
10L6depth=3 solutions=0Choose 3.
11L1depth=4 solutions=0Explore [1, 2, 3].
12L2depth=4 solutions=1Complete and valid: record [1, 2, 3].
13L8depth=3 solutions=1Unchoose 3: back to [1, 2].
14L8depth=2 solutions=1Unchoose 2: back to [1].
15L6depth=2 solutions=1Choose 3.
16L1depth=3 solutions=1Explore [1, 3].
17L4depth=4 solutions=1Reject 1: already on the path.
18L6depth=3 solutions=1Choose 2.
19L1depth=4 solutions=1Explore [1, 3, 2].
20L2depth=4 solutions=2Complete and valid: record [1, 3, 2].
21L8depth=3 solutions=2Unchoose 2: back to [1, 3].
22L4depth=4 solutions=2Reject 3: already on the path.
23L8depth=2 solutions=2Unchoose 3: back to [1].
24L8depth=1 solutions=2Unchoose 1: back to [].
25L6depth=1 solutions=2Choose 2.
26L1depth=2 solutions=2Explore [2].
27L6depth=2 solutions=2Choose 1.
28L1depth=3 solutions=2Explore [2, 1].
29L4depth=4 solutions=2Reject 1: already on the path.
30L4depth=4 solutions=2Reject 2: already on the path.
31L6depth=3 solutions=2Choose 3.
32L1depth=4 solutions=2Explore [2, 1, 3].
33L2depth=4 solutions=3Complete and valid: record [2, 1, 3].
34L8depth=3 solutions=3Unchoose 3: back to [2, 1].
35L8depth=2 solutions=3Unchoose 1: back to [2].
36L4depth=3 solutions=3Reject 2: already on the path.
37L6depth=2 solutions=3Choose 3.
38L1depth=3 solutions=3Explore [2, 3].
39L6depth=3 solutions=3Choose 1.
40L1depth=4 solutions=3Explore [2, 3, 1].
41L2depth=4 solutions=4Complete and valid: record [2, 3, 1].
42L8depth=3 solutions=4Unchoose 1: back to [2, 3].
43L4depth=4 solutions=4Reject 2: already on the path.
44L4depth=4 solutions=4Reject 3: already on the path.
45L8depth=2 solutions=4Unchoose 3: back to [2].
46L8depth=1 solutions=4Unchoose 2: back to [].
47L6depth=1 solutions=4Choose 3.
48L1depth=2 solutions=4Explore [3].
49L6depth=2 solutions=4Choose 1.
50L1depth=3 solutions=4Explore [3, 1].
51L4depth=4 solutions=4Reject 1: already on the path.
52L6depth=3 solutions=4Choose 2.
53L1depth=4 solutions=4Explore [3, 1, 2].
54L2depth=4 solutions=5Complete and valid: record [3, 1, 2].
55L8depth=3 solutions=5Unchoose 2: back to [3, 1].
56L4depth=4 solutions=5Reject 3: already on the path.
57L8depth=2 solutions=5Unchoose 1: back to [3].
58L6depth=2 solutions=5Choose 2.
59L1depth=3 solutions=5Explore [3, 2].
60L6depth=3 solutions=5Choose 1.
61L1depth=4 solutions=5Explore [3, 2, 1].
62L2depth=4 solutions=6Complete and valid: record [3, 2, 1].
63L8depth=3 solutions=6Unchoose 1: back to [3, 2].
64L4depth=4 solutions=6Reject 2: already on the path.
65L4depth=4 solutions=6Reject 3: already on the path.
66L8depth=2 solutions=6Unchoose 2: back to [3].
67L4depth=3 solutions=6Reject 3: already on the path.
68L8depth=1 solutions=6Unchoose 3: back to [].
69L10solutions=6 nodes=31 return=[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]Search finished: 6 solutions, 30 choices tried. Backtracking explores every branch the pruning rules allow.
20.01.5

Pruning

**Pruning** abandons a branch as soon as it provably cannot lead to a solution, so the whole subtree below it is never visited. The earlier the rule fires, the more it saves. For combinations of size k from n values, a branch that has chosen j values and has only m values left can reach size k only if j + m ≥ k; otherwise stop.

Run the “Pruning on” and “Pruning off (same answers, more work)” presets: both list the same 10 combinations, but with pruning the tree has 24 nodes instead of 26. The saving grows quickly with the size of the input: pruning never changes the answers, only the work. A correct prune must be *safe*: it may only cut branches that contain no solution.

Java
1static void combine(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out, boolean prune) {
2 if (path.size() == k) { out.add(new ArrayList<>(path)); return; }
3 for (int i = start; i < a.length; i++) {
4 if (prune && path.size() + (a.length - i) < k) break; // too few values left to reach size k
5 path.add(a[i]); // choose
6 combine(a, k, i + 1, path, out, prune); // explore
7 path.remove(path.size() - 1); // unchoose
8 }
9}

Combinations of size k, with and without pruning

Interactive · edit the input

Your input

Try

partial solution

[]

solutions found (0)

none yet
Setup

Choose 3 of 5 values in increasing order so each combination appears once. Pruning is on: a branch is abandoned as soon as the remaining values cannot fill the combination.

Step 1 / 74
Java
1static void combine(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out, boolean prune) {
2 if (path.size() == k) { out.add(new ArrayList<>(path)); return; }
3 for (int i = start; i < a.length; i++) {
4 if (prune && path.size() + (a.length - i) < k) break; // too few values left to reach size k
5 path.add(a[i]); // choose
6 combine(a, k, i + 1, path, out, prune); // explore
7 path.remove(path.size() - 1); // unchoose
8 }
9}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 74 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Choose 3 of 5 values in increasing order so each combination appears once. Pruning is on: a branch is abandoned as soon as the remaining values cannot fill the combination.
2L1depth=1 solutions=0Explore [].
3L5depth=1 solutions=0Choose 1.
4L1depth=2 solutions=0Explore [1].
5L5depth=2 solutions=0Choose 2.
6L1depth=3 solutions=0Explore [1, 2].
7L5depth=3 solutions=0Choose 3.
8L1depth=4 solutions=0Explore [1, 2, 3].
9L2depth=4 solutions=1Complete and valid: record [1, 2, 3].
10L7depth=3 solutions=1Unchoose 3: back to [1, 2].
11L5depth=3 solutions=1Choose 4.
12L1depth=4 solutions=1Explore [1, 2, 4].
13L2depth=4 solutions=2Complete and valid: record [1, 2, 4].
14L7depth=3 solutions=2Unchoose 4: back to [1, 2].
15L5depth=3 solutions=2Choose 5.
16L1depth=4 solutions=2Explore [1, 2, 5].
17L2depth=4 solutions=3Complete and valid: record [1, 2, 5].
18L7depth=3 solutions=3Unchoose 5: back to [1, 2].
19L7depth=2 solutions=3Unchoose 2: back to [1].
20L5depth=2 solutions=3Choose 3.
21L1depth=3 solutions=3Explore [1, 3].
22L5depth=3 solutions=3Choose 4.
23L1depth=4 solutions=3Explore [1, 3, 4].
24L2depth=4 solutions=4Complete and valid: record [1, 3, 4].
25L7depth=3 solutions=4Unchoose 4: back to [1, 3].
26L5depth=3 solutions=4Choose 5.
27L1depth=4 solutions=4Explore [1, 3, 5].
28L2depth=4 solutions=5Complete and valid: record [1, 3, 5].
29L7depth=3 solutions=5Unchoose 5: back to [1, 3].
30L7depth=2 solutions=5Unchoose 3: back to [1].
31L5depth=2 solutions=5Choose 4.
32L1depth=3 solutions=5Explore [1, 4].
33L5depth=3 solutions=5Choose 5.
34L1depth=4 solutions=5Explore [1, 4, 5].
35L2depth=4 solutions=6Complete and valid: record [1, 4, 5].
36L7depth=3 solutions=6Unchoose 5: back to [1, 4].
37L7depth=2 solutions=6Unchoose 4: back to [1].
38L4depth=3 solutions=6Reject 5: only 1 value left but 2 more are needed.
39L7depth=1 solutions=6Unchoose 1: back to [].
40L5depth=1 solutions=6Choose 2.
41L1depth=2 solutions=6Explore [2].
42L5depth=2 solutions=6Choose 3.
43L1depth=3 solutions=6Explore [2, 3].
44L5depth=3 solutions=6Choose 4.
45L1depth=4 solutions=6Explore [2, 3, 4].
46L2depth=4 solutions=7Complete and valid: record [2, 3, 4].
47L7depth=3 solutions=7Unchoose 4: back to [2, 3].
48L5depth=3 solutions=7Choose 5.
49L1depth=4 solutions=7Explore [2, 3, 5].
50L2depth=4 solutions=8Complete and valid: record [2, 3, 5].
51L7depth=3 solutions=8Unchoose 5: back to [2, 3].
52L7depth=2 solutions=8Unchoose 3: back to [2].
53L5depth=2 solutions=8Choose 4.
54L1depth=3 solutions=8Explore [2, 4].
55L5depth=3 solutions=8Choose 5.
56L1depth=4 solutions=8Explore [2, 4, 5].
57L2depth=4 solutions=9Complete and valid: record [2, 4, 5].
58L7depth=3 solutions=9Unchoose 5: back to [2, 4].
59L7depth=2 solutions=9Unchoose 4: back to [2].
60L4depth=3 solutions=9Reject 5: only 1 value left but 2 more are needed.
61L7depth=1 solutions=9Unchoose 2: back to [].
62L5depth=1 solutions=9Choose 3.
63L1depth=2 solutions=9Explore [3].
64L5depth=2 solutions=9Choose 4.
65L1depth=3 solutions=9Explore [3, 4].
66L5depth=3 solutions=9Choose 5.
67L1depth=4 solutions=9Explore [3, 4, 5].
68L2depth=4 solutions=10Complete and valid: record [3, 4, 5].
69L7depth=3 solutions=10Unchoose 5: back to [3, 4].
70L7depth=2 solutions=10Unchoose 4: back to [3].
71L4depth=3 solutions=10Reject 5: only 1 value left but 2 more are needed.
72L7depth=1 solutions=10Unchoose 3: back to [].
73L4depth=2 solutions=10Reject 4: only 2 values left but 3 more are needed.
74L9solutions=10 nodes=24 return=[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]Search finished: 10 solutions, 23 choices tried. Backtracking explores every branch the pruning rules allow.
20.01.6

Validity checks

A **validity check** decides whether an option may be added to the partial solution. Put it *before* the choose step, so an invalid option never enters the tree. For parentheses, the rules are: an opening parenthesis is allowed while fewer than n are used; a closing one is allowed only while something is open. With those two checks every string that reaches length 2n is balanced, and no balance test is needed at the end.

In the animation, rejected options appear as ✗ nodes with the reason in the log. For n = 3 the tree has 35 nodes and 5 solutions; without the checks the tree would contain all 2^6 = 64 strings of six parentheses at its leaves.

Java
1static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {
2 if (sb.length() == 2 * n) { out.add(sb.toString()); return; }
3 if (open < n) { // an opening parenthesis is still available
4 sb.append('(');
5 gen(open + 1, close, n, sb, out);
6 sb.deleteCharAt(sb.length() - 1);
7 }
8 if (close < open) { // a closing one is valid only if something is open
9 sb.append(')');
10 gen(open, close + 1, n, sb, out);
11 sb.deleteCharAt(sb.length() - 1);
12 }
13}

Generate valid parentheses

Interactive · edit the input

Your input

Try

partial solution

""

solutions found (0)

none yet
Setup

Build strings of 3 pairs one character at a time. Two validity rules prune the tree: never use more than 3 "(", and never write ")" unless an earlier "(" is still open. Every string that survives is balanced.

Step 1 / 84
Java
1static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {
2 if (sb.length() == 2 * n) { out.add(sb.toString()); return; }
3 if (open < n) { // an opening parenthesis is still available
4 sb.append('('); // choose
5 gen(open + 1, close, n, sb, out); // explore
6 sb.deleteCharAt(sb.length() - 1); // unchoose
7 }
8 if (close < open) { // a closing one is valid only if something is open
9 sb.append(')');
10 gen(open, close + 1, n, sb, out);
11 sb.deleteCharAt(sb.length() - 1);
12 }
13}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 84 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Build strings of 3 pairs one character at a time. Two validity rules prune the tree: never use more than 3 "(", and never write ")" unless an earlier "(" is still open. Every string that survives is balanced.
2L1depth=1 solutions=0Explore "".
3L4depth=1 solutions=0Choose (.
4L1depth=2 solutions=0Explore "(".
5L4depth=2 solutions=0Choose (.
6L1depth=3 solutions=0Explore "((".
7L4depth=3 solutions=0Choose (.
8L1depth=4 solutions=0Explore "(((".
9L3depth=5 solutions=0Reject (: all 3 opening parentheses are already used.
10L9depth=4 solutions=0Choose ).
11L1depth=5 solutions=0Explore "((()".
12L3depth=6 solutions=0Reject (: all 3 opening parentheses are already used.
13L9depth=5 solutions=0Choose ).
14L1depth=6 solutions=0Explore "((())".
15L3depth=7 solutions=0Reject (: all 3 opening parentheses are already used.
16L9depth=6 solutions=0Choose ).
17L1depth=7 solutions=0Explore "((()))".
18L2depth=7 solutions=1Complete and valid: record "((()))".
19L11depth=6 solutions=1Unchoose ): back to "((())".
20L11depth=5 solutions=1Unchoose ): back to "((()".
21L11depth=4 solutions=1Unchoose ): back to "(((".
22L6depth=3 solutions=1Unchoose (: back to "((".
23L9depth=3 solutions=1Choose ).
24L1depth=4 solutions=1Explore "(()".
25L4depth=4 solutions=1Choose (.
26L1depth=5 solutions=1Explore "(()(".
27L3depth=6 solutions=1Reject (: all 3 opening parentheses are already used.
28L9depth=5 solutions=1Choose ).
29L1depth=6 solutions=1Explore "(()()".
30L3depth=7 solutions=1Reject (: all 3 opening parentheses are already used.
31L9depth=6 solutions=1Choose ).
32L1depth=7 solutions=1Explore "(()())".
33L2depth=7 solutions=2Complete and valid: record "(()())".
34L11depth=6 solutions=2Unchoose ): back to "(()()".
35L11depth=5 solutions=2Unchoose ): back to "(()(".
36L6depth=4 solutions=2Unchoose (: back to "(()".
37L9depth=4 solutions=2Choose ).
38L1depth=5 solutions=2Explore "(())".
39L4depth=5 solutions=2Choose (.
40L1depth=6 solutions=2Explore "(())(".
41L3depth=7 solutions=2Reject (: all 3 opening parentheses are already used.
42L9depth=6 solutions=2Choose ).
43L1depth=7 solutions=2Explore "(())()".
44L2depth=7 solutions=3Complete and valid: record "(())()".
45L11depth=6 solutions=3Unchoose ): back to "(())(".
46L6depth=5 solutions=3Unchoose (: back to "(())".
47L8depth=6 solutions=3Reject ): there is nothing open to close.
48L11depth=4 solutions=3Unchoose ): back to "(()".
49L11depth=3 solutions=3Unchoose ): back to "((".
50L6depth=2 solutions=3Unchoose (: back to "(".
51L9depth=2 solutions=3Choose ).
52L1depth=3 solutions=3Explore "()".
53L4depth=3 solutions=3Choose (.
54L1depth=4 solutions=3Explore "()(".
55L4depth=4 solutions=3Choose (.
56L1depth=5 solutions=3Explore "()((".
57L3depth=6 solutions=3Reject (: all 3 opening parentheses are already used.
58L9depth=5 solutions=3Choose ).
59L1depth=6 solutions=3Explore "()(()".
60L3depth=7 solutions=3Reject (: all 3 opening parentheses are already used.
61L9depth=6 solutions=3Choose ).
62L1depth=7 solutions=3Explore "()(())".
63L2depth=7 solutions=4Complete and valid: record "()(())".
64L11depth=6 solutions=4Unchoose ): back to "()(()".
65L11depth=5 solutions=4Unchoose ): back to "()((".
66L6depth=4 solutions=4Unchoose (: back to "()(".
67L9depth=4 solutions=4Choose ).
68L1depth=5 solutions=4Explore "()()".
69L4depth=5 solutions=4Choose (.
70L1depth=6 solutions=4Explore "()()(".
71L3depth=7 solutions=4Reject (: all 3 opening parentheses are already used.
72L9depth=6 solutions=4Choose ).
73L1depth=7 solutions=4Explore "()()()".
74L2depth=7 solutions=5Complete and valid: record "()()()".
75L11depth=6 solutions=5Unchoose ): back to "()()(".
76L6depth=5 solutions=5Unchoose (: back to "()()".
77L8depth=6 solutions=5Reject ): there is nothing open to close.
78L11depth=4 solutions=5Unchoose ): back to "()(".
79L6depth=3 solutions=5Unchoose (: back to "()".
80L8depth=4 solutions=5Reject ): there is nothing open to close.
81L11depth=2 solutions=5Unchoose ): back to "(".
82L6depth=1 solutions=5Unchoose (: back to "".
83L8depth=2 solutions=5Reject ): there is nothing open to close.
84L13solutions=5 nodes=35 return=["((()))", "(()())", "(())()", "()(())", "()()()"]Search finished: 5 solutions, 34 choices tried. Backtracking explores every branch the pruning rules allow.

Practice ladder

Now try it yourself

Easy

  • Subsets
  • Generate Parentheses (n = 3 by hand)

Medium

  • Combinations
  • Permutations

Advanced

  • N-Queens
  • Sudoku Solver

Key takeaways

  • Backtracking is a depth-first search of an implicit decision tree: choose, explore, unchoose.
  • Undo exactly what you changed, and copy partial solutions when you record them.
  • Validity checks and safe pruning keep whole subtrees from ever being built.