Introduction to Backtracking

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.

start

0

1

00

01

10

11

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:

  1. If the current string has length n, print it and return. This is reaching the maze exit.
  2. Choose 0. Add it to the current string.
  3. Explore. Call the function again to fill the rest.
  4. Un-choose 0. Remove it so the spot is free.
  5. 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.

binary_strings.py
# current is the string built so far, n is the target length
def 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 = 2
generate("", n)

The output of the above code will be:

00
01
10
11

Note

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 0 again, now "00". Length is 2, so print 00. Come back.
  • Un-choose, back to "0". Choose 1, now "01". Print 01. Come back.
  • Un-choose twice, back to "". Choose 1, now "1". Go deeper.
  • Choose 0, now "10". Print 10. Come back. Un-choose, choose 1, "11". Print 11.

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. 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. 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. 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. 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.

πŸš€ What’s Next?

Share & Connect