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
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.
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 inputpartial solution
solutions found (0)
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.
1static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> out) {2out.add(new ArrayList<>(path)); // every partial solution is a subset3for (int i = start; i < a.length; i++) {4path.add(a[i]); // choose5subsets(a, i + 1, path, out); // explore6path.remove(path.size() - 1); // unchoose7}8}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | 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. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L2 | depth=1 solutions=1 | Record []: every partial solution counts here. |
| 4 | L4 | depth=1 solutions=1 | Choose 1. |
| 5 | L1 | depth=2 solutions=1 | Explore [1]. |
| 6 | L2 | depth=2 solutions=2 | Record [1]: every partial solution counts here. |
| 7 | L4 | depth=2 solutions=2 | Choose 2. |
| 8 | L1 | depth=3 solutions=2 | Explore [1, 2]. |
| 9 | L2 | depth=3 solutions=3 | Record [1, 2]: every partial solution counts here. |
| 10 | L4 | depth=3 solutions=3 | Choose 3. |
| 11 | L1 | depth=4 solutions=3 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=4 | Record [1, 2, 3]: every partial solution counts here. |
| 13 | L6 | depth=3 solutions=4 | Unchoose 3: back to [1, 2]. |
| 14 | L6 | depth=2 solutions=4 | Unchoose 2: back to [1]. |
| 15 | L4 | depth=2 solutions=4 | Choose 3. |
| 16 | L1 | depth=3 solutions=4 | Explore [1, 3]. |
| 17 | L2 | depth=3 solutions=5 | Record [1, 3]: every partial solution counts here. |
| 18 | L6 | depth=2 solutions=5 | Unchoose 3: back to [1]. |
| 19 | L6 | depth=1 solutions=5 | Unchoose 1: back to []. |
| 20 | L4 | depth=1 solutions=5 | Choose 2. |
| 21 | L1 | depth=2 solutions=5 | Explore [2]. |
| 22 | L2 | depth=2 solutions=6 | Record [2]: every partial solution counts here. |
| 23 | L4 | depth=2 solutions=6 | Choose 3. |
| 24 | L1 | depth=3 solutions=6 | Explore [2, 3]. |
| 25 | L2 | depth=3 solutions=7 | Record [2, 3]: every partial solution counts here. |
| 26 | L6 | depth=2 solutions=7 | Unchoose 3: back to [2]. |
| 27 | L6 | depth=1 solutions=7 | Unchoose 2: back to []. |
| 28 | L4 | depth=1 solutions=7 | Choose 3. |
| 29 | L1 | depth=2 solutions=7 | Explore [3]. |
| 30 | L2 | depth=2 solutions=8 | Record [3]: every partial solution counts here. |
| 31 | L6 | depth=1 solutions=8 | Unchoose 3: back to []. |
| 32 | L8 | solutions=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. |
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.
1interface Search<O> {2boolean complete(); // is the partial solution a full answer?3List<O> options(); // the choices available right now4void apply(O option); // choose5void undo(O option); // unchoose: the exact inverse of apply6String snapshot(); // a copy of the current partial solution7}8static <O> void solve(Search<O> s, List<String> out) {9if (s.complete()) { out.add(s.snapshot()); return; }10for (O o : s.options()) {11s.apply(o); // choose12solve(s, out); // explore13s.undo(o); // unchoose: restore the state exactly14}15}
Subsets (power set) by backtracking
Interactive · edit the inputpartial solution
solutions found (0)
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.
1static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> out) {2out.add(new ArrayList<>(path)); // every partial solution is a subset3for (int i = start; i < a.length; i++) {4path.add(a[i]); // choose5subsets(a, i + 1, path, out); // explore6path.remove(path.size() - 1); // unchoose7}8}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | 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. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L2 | depth=1 solutions=1 | Record []: every partial solution counts here. |
| 4 | L4 | depth=1 solutions=1 | Choose 1. |
| 5 | L1 | depth=2 solutions=1 | Explore [1]. |
| 6 | L2 | depth=2 solutions=2 | Record [1]: every partial solution counts here. |
| 7 | L4 | depth=2 solutions=2 | Choose 2. |
| 8 | L1 | depth=3 solutions=2 | Explore [1, 2]. |
| 9 | L2 | depth=3 solutions=3 | Record [1, 2]: every partial solution counts here. |
| 10 | L4 | depth=3 solutions=3 | Choose 3. |
| 11 | L1 | depth=4 solutions=3 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=4 | Record [1, 2, 3]: every partial solution counts here. |
| 13 | L6 | depth=3 solutions=4 | Unchoose 3: back to [1, 2]. |
| 14 | L6 | depth=2 solutions=4 | Unchoose 2: back to [1]. |
| 15 | L4 | depth=2 solutions=4 | Choose 3. |
| 16 | L1 | depth=3 solutions=4 | Explore [1, 3]. |
| 17 | L2 | depth=3 solutions=5 | Record [1, 3]: every partial solution counts here. |
| 18 | L6 | depth=2 solutions=5 | Unchoose 3: back to [1]. |
| 19 | L6 | depth=1 solutions=5 | Unchoose 1: back to []. |
| 20 | L4 | depth=1 solutions=5 | Choose 2. |
| 21 | L1 | depth=2 solutions=5 | Explore [2]. |
| 22 | L2 | depth=2 solutions=6 | Record [2]: every partial solution counts here. |
| 23 | L4 | depth=2 solutions=6 | Choose 3. |
| 24 | L1 | depth=3 solutions=6 | Explore [2, 3]. |
| 25 | L2 | depth=3 solutions=7 | Record [2, 3]: every partial solution counts here. |
| 26 | L6 | depth=2 solutions=7 | Unchoose 3: back to [2]. |
| 27 | L6 | depth=1 solutions=7 | Unchoose 2: back to []. |
| 28 | L4 | depth=1 solutions=7 | Choose 3. |
| 29 | L1 | depth=2 solutions=7 | Explore [3]. |
| 30 | L2 | depth=2 solutions=8 | Record [3]: every partial solution counts here. |
| 31 | L6 | depth=1 solutions=8 | Unchoose 3: back to []. |
| 32 | L8 | solutions=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. |
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.
1static List<List<Integer>> combinations(int[] a, int k) {2List<List<Integer>> out = new ArrayList<>();3build(a, k, 0, new ArrayList<>(), out);4return out;5}6private static void build(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out) {7if (path.size() == k) { out.add(new ArrayList<>(path)); return; } // copy the partial solution8for (int i = start; i < a.length; i++) {9path.add(a[i]); // choose10build(a, k, i + 1, path, out); // explore11path.remove(path.size() - 1); // unchoose12}13}
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.
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out) {2if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }3for (int i = 0; i < a.length; i++) {4if (used[i]) continue; // already on the path5used[i] = true; path.add(a[i]); // choose: two pieces of state change6permute(a, used, path, out);7path.remove(path.size() - 1); used[i] = false; // unchoose: both are restored8}9}
Permutations, including unique permutations with duplicates
Interactive · edit the inputpartial solution
solutions found (0)
at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings.
1static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> out, boolean unique) { // sort a first when unique2if (path.size() == a.length) { out.add(new ArrayList<>(path)); return; }3for (int i = 0; i < a.length; i++) {4if (used[i]) continue; // already on the path5if (unique && i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue; // a twin of the previous value, which is free6used[i] = true; path.add(a[i]); // choose7permute(a, used, path, out, unique); // explore8path.remove(path.size() - 1); used[i] = false; // unchoose9}10}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 69 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | at every level try each value not yet used (the used[] array is the undo state). 3 values have 3! = 6 orderings. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L6 | depth=1 solutions=0 | Choose 1. |
| 4 | L1 | depth=2 solutions=0 | Explore [1]. |
| 5 | L4 | depth=3 solutions=0 | Reject 1: already on the path. |
| 6 | L6 | depth=2 solutions=0 | Choose 2. |
| 7 | L1 | depth=3 solutions=0 | Explore [1, 2]. |
| 8 | L4 | depth=4 solutions=0 | Reject 1: already on the path. |
| 9 | L4 | depth=4 solutions=0 | Reject 2: already on the path. |
| 10 | L6 | depth=3 solutions=0 | Choose 3. |
| 11 | L1 | depth=4 solutions=0 | Explore [1, 2, 3]. |
| 12 | L2 | depth=4 solutions=1 | Complete and valid: record [1, 2, 3]. |
| 13 | L8 | depth=3 solutions=1 | Unchoose 3: back to [1, 2]. |
| 14 | L8 | depth=2 solutions=1 | Unchoose 2: back to [1]. |
| 15 | L6 | depth=2 solutions=1 | Choose 3. |
| 16 | L1 | depth=3 solutions=1 | Explore [1, 3]. |
| 17 | L4 | depth=4 solutions=1 | Reject 1: already on the path. |
| 18 | L6 | depth=3 solutions=1 | Choose 2. |
| 19 | L1 | depth=4 solutions=1 | Explore [1, 3, 2]. |
| 20 | L2 | depth=4 solutions=2 | Complete and valid: record [1, 3, 2]. |
| 21 | L8 | depth=3 solutions=2 | Unchoose 2: back to [1, 3]. |
| 22 | L4 | depth=4 solutions=2 | Reject 3: already on the path. |
| 23 | L8 | depth=2 solutions=2 | Unchoose 3: back to [1]. |
| 24 | L8 | depth=1 solutions=2 | Unchoose 1: back to []. |
| 25 | L6 | depth=1 solutions=2 | Choose 2. |
| 26 | L1 | depth=2 solutions=2 | Explore [2]. |
| 27 | L6 | depth=2 solutions=2 | Choose 1. |
| 28 | L1 | depth=3 solutions=2 | Explore [2, 1]. |
| 29 | L4 | depth=4 solutions=2 | Reject 1: already on the path. |
| 30 | L4 | depth=4 solutions=2 | Reject 2: already on the path. |
| 31 | L6 | depth=3 solutions=2 | Choose 3. |
| 32 | L1 | depth=4 solutions=2 | Explore [2, 1, 3]. |
| 33 | L2 | depth=4 solutions=3 | Complete and valid: record [2, 1, 3]. |
| 34 | L8 | depth=3 solutions=3 | Unchoose 3: back to [2, 1]. |
| 35 | L8 | depth=2 solutions=3 | Unchoose 1: back to [2]. |
| 36 | L4 | depth=3 solutions=3 | Reject 2: already on the path. |
| 37 | L6 | depth=2 solutions=3 | Choose 3. |
| 38 | L1 | depth=3 solutions=3 | Explore [2, 3]. |
| 39 | L6 | depth=3 solutions=3 | Choose 1. |
| 40 | L1 | depth=4 solutions=3 | Explore [2, 3, 1]. |
| 41 | L2 | depth=4 solutions=4 | Complete and valid: record [2, 3, 1]. |
| 42 | L8 | depth=3 solutions=4 | Unchoose 1: back to [2, 3]. |
| 43 | L4 | depth=4 solutions=4 | Reject 2: already on the path. |
| 44 | L4 | depth=4 solutions=4 | Reject 3: already on the path. |
| 45 | L8 | depth=2 solutions=4 | Unchoose 3: back to [2]. |
| 46 | L8 | depth=1 solutions=4 | Unchoose 2: back to []. |
| 47 | L6 | depth=1 solutions=4 | Choose 3. |
| 48 | L1 | depth=2 solutions=4 | Explore [3]. |
| 49 | L6 | depth=2 solutions=4 | Choose 1. |
| 50 | L1 | depth=3 solutions=4 | Explore [3, 1]. |
| 51 | L4 | depth=4 solutions=4 | Reject 1: already on the path. |
| 52 | L6 | depth=3 solutions=4 | Choose 2. |
| 53 | L1 | depth=4 solutions=4 | Explore [3, 1, 2]. |
| 54 | L2 | depth=4 solutions=5 | Complete and valid: record [3, 1, 2]. |
| 55 | L8 | depth=3 solutions=5 | Unchoose 2: back to [3, 1]. |
| 56 | L4 | depth=4 solutions=5 | Reject 3: already on the path. |
| 57 | L8 | depth=2 solutions=5 | Unchoose 1: back to [3]. |
| 58 | L6 | depth=2 solutions=5 | Choose 2. |
| 59 | L1 | depth=3 solutions=5 | Explore [3, 2]. |
| 60 | L6 | depth=3 solutions=5 | Choose 1. |
| 61 | L1 | depth=4 solutions=5 | Explore [3, 2, 1]. |
| 62 | L2 | depth=4 solutions=6 | Complete and valid: record [3, 2, 1]. |
| 63 | L8 | depth=3 solutions=6 | Unchoose 1: back to [3, 2]. |
| 64 | L4 | depth=4 solutions=6 | Reject 2: already on the path. |
| 65 | L4 | depth=4 solutions=6 | Reject 3: already on the path. |
| 66 | L8 | depth=2 solutions=6 | Unchoose 2: back to [3]. |
| 67 | L4 | depth=3 solutions=6 | Reject 3: already on the path. |
| 68 | L8 | depth=1 solutions=6 | Unchoose 3: back to []. |
| 69 | L10 | solutions=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. |
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.
1static void combine(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out, boolean prune) {2if (path.size() == k) { out.add(new ArrayList<>(path)); return; }3for (int i = start; i < a.length; i++) {4if (prune && path.size() + (a.length - i) < k) break; // too few values left to reach size k5path.add(a[i]); // choose6combine(a, k, i + 1, path, out, prune); // explore7path.remove(path.size() - 1); // unchoose8}9}
Combinations of size k, with and without pruning
Interactive · edit the inputpartial solution
solutions found (0)
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.
1static void combine(int[] a, int k, int start, List<Integer> path, List<List<Integer>> out, boolean prune) {2if (path.size() == k) { out.add(new ArrayList<>(path)); return; }3for (int i = start; i < a.length; i++) {4if (prune && path.size() + (a.length - i) < k) break; // too few values left to reach size k5path.add(a[i]); // choose6combine(a, k, i + 1, path, out, prune); // explore7path.remove(path.size() - 1); // unchoose8}9}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 74 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | 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. |
| 2 | L1 | depth=1 solutions=0 | Explore []. |
| 3 | L5 | depth=1 solutions=0 | Choose 1. |
| 4 | L1 | depth=2 solutions=0 | Explore [1]. |
| 5 | L5 | depth=2 solutions=0 | Choose 2. |
| 6 | L1 | depth=3 solutions=0 | Explore [1, 2]. |
| 7 | L5 | depth=3 solutions=0 | Choose 3. |
| 8 | L1 | depth=4 solutions=0 | Explore [1, 2, 3]. |
| 9 | L2 | depth=4 solutions=1 | Complete and valid: record [1, 2, 3]. |
| 10 | L7 | depth=3 solutions=1 | Unchoose 3: back to [1, 2]. |
| 11 | L5 | depth=3 solutions=1 | Choose 4. |
| 12 | L1 | depth=4 solutions=1 | Explore [1, 2, 4]. |
| 13 | L2 | depth=4 solutions=2 | Complete and valid: record [1, 2, 4]. |
| 14 | L7 | depth=3 solutions=2 | Unchoose 4: back to [1, 2]. |
| 15 | L5 | depth=3 solutions=2 | Choose 5. |
| 16 | L1 | depth=4 solutions=2 | Explore [1, 2, 5]. |
| 17 | L2 | depth=4 solutions=3 | Complete and valid: record [1, 2, 5]. |
| 18 | L7 | depth=3 solutions=3 | Unchoose 5: back to [1, 2]. |
| 19 | L7 | depth=2 solutions=3 | Unchoose 2: back to [1]. |
| 20 | L5 | depth=2 solutions=3 | Choose 3. |
| 21 | L1 | depth=3 solutions=3 | Explore [1, 3]. |
| 22 | L5 | depth=3 solutions=3 | Choose 4. |
| 23 | L1 | depth=4 solutions=3 | Explore [1, 3, 4]. |
| 24 | L2 | depth=4 solutions=4 | Complete and valid: record [1, 3, 4]. |
| 25 | L7 | depth=3 solutions=4 | Unchoose 4: back to [1, 3]. |
| 26 | L5 | depth=3 solutions=4 | Choose 5. |
| 27 | L1 | depth=4 solutions=4 | Explore [1, 3, 5]. |
| 28 | L2 | depth=4 solutions=5 | Complete and valid: record [1, 3, 5]. |
| 29 | L7 | depth=3 solutions=5 | Unchoose 5: back to [1, 3]. |
| 30 | L7 | depth=2 solutions=5 | Unchoose 3: back to [1]. |
| 31 | L5 | depth=2 solutions=5 | Choose 4. |
| 32 | L1 | depth=3 solutions=5 | Explore [1, 4]. |
| 33 | L5 | depth=3 solutions=5 | Choose 5. |
| 34 | L1 | depth=4 solutions=5 | Explore [1, 4, 5]. |
| 35 | L2 | depth=4 solutions=6 | Complete and valid: record [1, 4, 5]. |
| 36 | L7 | depth=3 solutions=6 | Unchoose 5: back to [1, 4]. |
| 37 | L7 | depth=2 solutions=6 | Unchoose 4: back to [1]. |
| 38 | L4 | depth=3 solutions=6 | Reject 5: only 1 value left but 2 more are needed. |
| 39 | L7 | depth=1 solutions=6 | Unchoose 1: back to []. |
| 40 | L5 | depth=1 solutions=6 | Choose 2. |
| 41 | L1 | depth=2 solutions=6 | Explore [2]. |
| 42 | L5 | depth=2 solutions=6 | Choose 3. |
| 43 | L1 | depth=3 solutions=6 | Explore [2, 3]. |
| 44 | L5 | depth=3 solutions=6 | Choose 4. |
| 45 | L1 | depth=4 solutions=6 | Explore [2, 3, 4]. |
| 46 | L2 | depth=4 solutions=7 | Complete and valid: record [2, 3, 4]. |
| 47 | L7 | depth=3 solutions=7 | Unchoose 4: back to [2, 3]. |
| 48 | L5 | depth=3 solutions=7 | Choose 5. |
| 49 | L1 | depth=4 solutions=7 | Explore [2, 3, 5]. |
| 50 | L2 | depth=4 solutions=8 | Complete and valid: record [2, 3, 5]. |
| 51 | L7 | depth=3 solutions=8 | Unchoose 5: back to [2, 3]. |
| 52 | L7 | depth=2 solutions=8 | Unchoose 3: back to [2]. |
| 53 | L5 | depth=2 solutions=8 | Choose 4. |
| 54 | L1 | depth=3 solutions=8 | Explore [2, 4]. |
| 55 | L5 | depth=3 solutions=8 | Choose 5. |
| 56 | L1 | depth=4 solutions=8 | Explore [2, 4, 5]. |
| 57 | L2 | depth=4 solutions=9 | Complete and valid: record [2, 4, 5]. |
| 58 | L7 | depth=3 solutions=9 | Unchoose 5: back to [2, 4]. |
| 59 | L7 | depth=2 solutions=9 | Unchoose 4: back to [2]. |
| 60 | L4 | depth=3 solutions=9 | Reject 5: only 1 value left but 2 more are needed. |
| 61 | L7 | depth=1 solutions=9 | Unchoose 2: back to []. |
| 62 | L5 | depth=1 solutions=9 | Choose 3. |
| 63 | L1 | depth=2 solutions=9 | Explore [3]. |
| 64 | L5 | depth=2 solutions=9 | Choose 4. |
| 65 | L1 | depth=3 solutions=9 | Explore [3, 4]. |
| 66 | L5 | depth=3 solutions=9 | Choose 5. |
| 67 | L1 | depth=4 solutions=9 | Explore [3, 4, 5]. |
| 68 | L2 | depth=4 solutions=10 | Complete and valid: record [3, 4, 5]. |
| 69 | L7 | depth=3 solutions=10 | Unchoose 5: back to [3, 4]. |
| 70 | L7 | depth=2 solutions=10 | Unchoose 4: back to [3]. |
| 71 | L4 | depth=3 solutions=10 | Reject 5: only 1 value left but 2 more are needed. |
| 72 | L7 | depth=1 solutions=10 | Unchoose 3: back to []. |
| 73 | L4 | depth=2 solutions=10 | Reject 4: only 2 values left but 3 more are needed. |
| 74 | L9 | solutions=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. |
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.
1static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {2if (sb.length() == 2 * n) { out.add(sb.toString()); return; }3if (open < n) { // an opening parenthesis is still available4sb.append('(');5gen(open + 1, close, n, sb, out);6sb.deleteCharAt(sb.length() - 1);7}8if (close < open) { // a closing one is valid only if something is open9sb.append(')');10gen(open, close + 1, n, sb, out);11sb.deleteCharAt(sb.length() - 1);12}13}
Generate valid parentheses
Interactive · edit the inputpartial solution
solutions found (0)
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.
1static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {2if (sb.length() == 2 * n) { out.add(sb.toString()); return; }3if (open < n) { // an opening parenthesis is still available4sb.append('('); // choose5gen(open + 1, close, n, sb, out); // explore6sb.deleteCharAt(sb.length() - 1); // unchoose7}8if (close < open) { // a closing one is valid only if something is open9sb.append(')');10gen(open, close + 1, n, sb, out);11sb.deleteCharAt(sb.length() - 1);12}13}
Live state
depth
0
solutions
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 84 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | depth=0 solutions=0 | 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. |
| 2 | L1 | depth=1 solutions=0 | Explore "". |
| 3 | L4 | depth=1 solutions=0 | Choose (. |
| 4 | L1 | depth=2 solutions=0 | Explore "(". |
| 5 | L4 | depth=2 solutions=0 | Choose (. |
| 6 | L1 | depth=3 solutions=0 | Explore "((". |
| 7 | L4 | depth=3 solutions=0 | Choose (. |
| 8 | L1 | depth=4 solutions=0 | Explore "(((". |
| 9 | L3 | depth=5 solutions=0 | Reject (: all 3 opening parentheses are already used. |
| 10 | L9 | depth=4 solutions=0 | Choose ). |
| 11 | L1 | depth=5 solutions=0 | Explore "((()". |
| 12 | L3 | depth=6 solutions=0 | Reject (: all 3 opening parentheses are already used. |
| 13 | L9 | depth=5 solutions=0 | Choose ). |
| 14 | L1 | depth=6 solutions=0 | Explore "((())". |
| 15 | L3 | depth=7 solutions=0 | Reject (: all 3 opening parentheses are already used. |
| 16 | L9 | depth=6 solutions=0 | Choose ). |
| 17 | L1 | depth=7 solutions=0 | Explore "((()))". |
| 18 | L2 | depth=7 solutions=1 | Complete and valid: record "((()))". |
| 19 | L11 | depth=6 solutions=1 | Unchoose ): back to "((())". |
| 20 | L11 | depth=5 solutions=1 | Unchoose ): back to "((()". |
| 21 | L11 | depth=4 solutions=1 | Unchoose ): back to "(((". |
| 22 | L6 | depth=3 solutions=1 | Unchoose (: back to "((". |
| 23 | L9 | depth=3 solutions=1 | Choose ). |
| 24 | L1 | depth=4 solutions=1 | Explore "(()". |
| 25 | L4 | depth=4 solutions=1 | Choose (. |
| 26 | L1 | depth=5 solutions=1 | Explore "(()(". |
| 27 | L3 | depth=6 solutions=1 | Reject (: all 3 opening parentheses are already used. |
| 28 | L9 | depth=5 solutions=1 | Choose ). |
| 29 | L1 | depth=6 solutions=1 | Explore "(()()". |
| 30 | L3 | depth=7 solutions=1 | Reject (: all 3 opening parentheses are already used. |
| 31 | L9 | depth=6 solutions=1 | Choose ). |
| 32 | L1 | depth=7 solutions=1 | Explore "(()())". |
| 33 | L2 | depth=7 solutions=2 | Complete and valid: record "(()())". |
| 34 | L11 | depth=6 solutions=2 | Unchoose ): back to "(()()". |
| 35 | L11 | depth=5 solutions=2 | Unchoose ): back to "(()(". |
| 36 | L6 | depth=4 solutions=2 | Unchoose (: back to "(()". |
| 37 | L9 | depth=4 solutions=2 | Choose ). |
| 38 | L1 | depth=5 solutions=2 | Explore "(())". |
| 39 | L4 | depth=5 solutions=2 | Choose (. |
| 40 | L1 | depth=6 solutions=2 | Explore "(())(". |
| 41 | L3 | depth=7 solutions=2 | Reject (: all 3 opening parentheses are already used. |
| 42 | L9 | depth=6 solutions=2 | Choose ). |
| 43 | L1 | depth=7 solutions=2 | Explore "(())()". |
| 44 | L2 | depth=7 solutions=3 | Complete and valid: record "(())()". |
| 45 | L11 | depth=6 solutions=3 | Unchoose ): back to "(())(". |
| 46 | L6 | depth=5 solutions=3 | Unchoose (: back to "(())". |
| 47 | L8 | depth=6 solutions=3 | Reject ): there is nothing open to close. |
| 48 | L11 | depth=4 solutions=3 | Unchoose ): back to "(()". |
| 49 | L11 | depth=3 solutions=3 | Unchoose ): back to "((". |
| 50 | L6 | depth=2 solutions=3 | Unchoose (: back to "(". |
| 51 | L9 | depth=2 solutions=3 | Choose ). |
| 52 | L1 | depth=3 solutions=3 | Explore "()". |
| 53 | L4 | depth=3 solutions=3 | Choose (. |
| 54 | L1 | depth=4 solutions=3 | Explore "()(". |
| 55 | L4 | depth=4 solutions=3 | Choose (. |
| 56 | L1 | depth=5 solutions=3 | Explore "()((". |
| 57 | L3 | depth=6 solutions=3 | Reject (: all 3 opening parentheses are already used. |
| 58 | L9 | depth=5 solutions=3 | Choose ). |
| 59 | L1 | depth=6 solutions=3 | Explore "()(()". |
| 60 | L3 | depth=7 solutions=3 | Reject (: all 3 opening parentheses are already used. |
| 61 | L9 | depth=6 solutions=3 | Choose ). |
| 62 | L1 | depth=7 solutions=3 | Explore "()(())". |
| 63 | L2 | depth=7 solutions=4 | Complete and valid: record "()(())". |
| 64 | L11 | depth=6 solutions=4 | Unchoose ): back to "()(()". |
| 65 | L11 | depth=5 solutions=4 | Unchoose ): back to "()((". |
| 66 | L6 | depth=4 solutions=4 | Unchoose (: back to "()(". |
| 67 | L9 | depth=4 solutions=4 | Choose ). |
| 68 | L1 | depth=5 solutions=4 | Explore "()()". |
| 69 | L4 | depth=5 solutions=4 | Choose (. |
| 70 | L1 | depth=6 solutions=4 | Explore "()()(". |
| 71 | L3 | depth=7 solutions=4 | Reject (: all 3 opening parentheses are already used. |
| 72 | L9 | depth=6 solutions=4 | Choose ). |
| 73 | L1 | depth=7 solutions=4 | Explore "()()()". |
| 74 | L2 | depth=7 solutions=5 | Complete and valid: record "()()()". |
| 75 | L11 | depth=6 solutions=5 | Unchoose ): back to "()()(". |
| 76 | L6 | depth=5 solutions=5 | Unchoose (: back to "()()". |
| 77 | L8 | depth=6 solutions=5 | Reject ): there is nothing open to close. |
| 78 | L11 | depth=4 solutions=5 | Unchoose ): back to "()(". |
| 79 | L6 | depth=3 solutions=5 | Unchoose (: back to "()". |
| 80 | L8 | depth=4 solutions=5 | Reject ): there is nothing open to close. |
| 81 | L11 | depth=2 solutions=5 | Unchoose ): back to "(". |
| 82 | L6 | depth=1 solutions=5 | Unchoose (: back to "". |
| 83 | L8 | depth=2 solutions=5 | Reject ): there is nothing open to close. |
| 84 | L13 | solutions=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.