Introduction to Backtracking
Table of Contents + β
Some problems ask you to find every possible answer. Make all the passwords of length 3. List all the ways to seat people. Pick every group you can form from a set. You cannot just guess one answer here. You have to try things. When a try goes wrong, you step back and try the next thing. That stepping back is the whole idea. It is called backtracking.
π§ A maze tells the story best
Picture Riya standing at the start of a maze. She picks a path and walks. Then she hits a wall. So what does she do? She does not give up and go home. She walks back to the last spot where the path split into two. Then she takes the other turn she had not tried yet.
That walking back to the last choice point is backtracking. Backtracking means undoing your last move so you can try a different one.
So the rhythm is simple. Pick a turn. Walk forward. If you hit a wall, walk back. Pick a different turn. Keep doing this until you find the exit. Or until you have tried every path.
A computer does the exact same thing when it solves these problems. It just calls the choice points βstatesβ and the walking back βreturning from recursionβ.
π― What backtracking really is
Backtracking is a way to search through all the choices a problem gives you, one step at a time. You build the answer piece by piece. At each step you make one choice. Then you go deeper and make the next choice. If the path you are on cannot work, you throw away the last choice and try another.
Here is the thing. It is just smart trial and error. You try. You check. You undo. You try again.
The pattern always runs in the same order:
- Choose β add one option to your current answer.
- Explore β go deeper and decide the rest by calling the function again. This is the recursion.
- Un-choose β remove that option so the spot is clean for the next option to try.
That last move is the one people forget. Un-choose is the βwalk back to the splitβ part of the maze. Without it your answer keeps the old choice stuck inside it. Then everything after that goes wrong.
Tip
A clean way to remember it: choose, explore, un-choose. Whatever you add before going deeper, you must remove after coming back.
π³ The decision tree
Every backtracking problem is really a decision tree. At each level you decide one thing. Each branch is one option. Walking down to a leaf gives you one complete answer.
Say you want every string of length 2 made of 0 and 1. At each spot you choose 0 or 1. Here is the tree of those choices.
See how every leaf at the bottom is one finished string? 00, 01, 10, 11. Backtracking walks down to a leaf, writes down the answer, then walks back up to the last split and goes down the next branch. That walk back up is the un-choose step.
π§© A tiny example: all binary strings of length n
Let us build every binary string of length n. A binary string is just a string made only of the characters 0 and 1. For n equal to 2 the answers are 00, 01, 10, 11.
The plan reads straight off the maze idea. We keep a current string we are building. When it reaches length n, that is a finished answer. So we print it. Otherwise we have a choice to make for the next character.
Steps the function follows:
- If the current string has length
n, print it and return. This is reaching the maze exit. - Choose
0. Add it to the current string. - Explore. Call the function again to fill the rest.
- Un-choose
0. Remove it so the spot is free. - Now do the same three moves for
1: choose, explore, un-choose.
Here is that idea in all five languages. Watch how each call adds one character, goes deeper, then removes it before trying the other character.
# current is the string built so far, n is the target lengthdef generate(current, n): if len(current) == n: # full length, this is one answer print(current) return # choose 0, explore, then move on to the next choice generate(current + "0", n) # choose 1, explore generate(current + "1", n)
n = 2generate("", n)The output of the above code will be:
00011011Note
Notice the Python and JavaScript versions never write a separate un-choose line. That is because they make a brand new string with current + β0β instead of changing one shared string. The old string stays untouched, so there is nothing to undo. In C++ and Java we share one builder to save memory, so there we must un-choose by hand.
π How a single path runs
It helps to watch one path go all the way down and come back. Take the Java or C++ version with the shared builder, for n equal to 2.
- Start with an empty string
"". - Choose
0, now"0". Go deeper. - Choose
0again, now"00". Length is 2, so print00. Come back. - Un-choose, back to
"0". Choose1, now"01". Print01. Come back. - Un-choose twice, back to
"". Choose1, now"1". Go deeper. - Choose
0, now"10". Print10. Come back. Un-choose, choose1,"11". Print11.
That up and down walk is backtracking doing its job. Every time we come back up, the un-choose step cleans the spot for the next option.
βοΈ Backtracking vs plain brute force
People often mix these up, so let us put them side by side.
| Plain brute force | Backtracking |
|---|---|
| Builds every full answer, then checks each one | Builds answers step by step |
| Cannot stop a bad path early | Drops a bad path the moment it sees it cannot work |
| Often slower because it wastes work | Often faster because it skips whole branches |
That skipping of whole branches has a name. It is called pruning. Pruning means cutting off a branch of the choice tree early because you already know nothing good can come from it. A maze walker who sees a dead end one step ahead does not keep walking into it. That saved walk is pruning.
β±οΈ A word on cost
Backtracking explores a tree of choices, so its cost grows with how many leaves that tree has. For binary strings of length n there are two choices at each of n spots. That gives 2^n answers. So this kind of problem is naturally slow when n gets big. And there is no way around printing every answer if every answer is asked for.
Caution
Backtracking can be slow because the number of paths can explode. Always look for a way to prune. If a half-built answer already breaks a rule, stop right there. Do not explore deeper. That one check can cut your work a lot.
β οΈ Common mistakes
A few traps catch almost everyone the first time:
- Forgetting to un-choose. With a shared list or builder, if you choose but never remove, the next branch starts dirty and your answers come out wrong.
- Un-choosing the wrong thing. Remove exactly what you added, nothing more. Adding one item and removing two leaves the state broken.
- No base case. Without the βif the answer is complete, record it and returnβ check, the recursion never stops and you crash.
- Saving a shared list by reference. If you store the same list object in your results, every entry points to the same thing and they all look the same. Save a copy.
π§© What Youβve Learned
- β Backtracking builds a solution step by step and undoes a step when it leads to a dead end.
- β The pattern is always choose, explore, un-choose β add an option, recurse, then remove it.
- β Every backtracking problem is a decision tree, and walking it to each leaf gives one complete answer.
- β The un-choose step is what frees the state so the next option starts clean.
- β Pruning drops bad branches early, which is what makes backtracking faster than plain brute force.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does the 'un-choose' step do in backtracking?
Why: Un-choose undoes the last move, just like walking back to the last split in a maze, so the next branch starts from a clean state.
- 2
Which three moves make up the backtracking pattern, in order?
Why: Backtracking always runs choose (add an option), explore (recurse), then un-choose (remove it).
- 3
Why is dropping a bad branch early (pruning) useful?
Why: If a half-built answer cannot work, exploring it deeper is wasted effort, so cutting it early saves a lot of time.
- 4
How many binary strings of length n does the example produce?
Why: There are two choices (0 or 1) at each of the n positions, so the total is 2^n strings.