Recursive Backtracking Explanation

Thanks to Lon Ingram for this explanation of recursive backtracking.

Backtracking problems are solved one step at a time.  Literally!  Here's the general algorithm:

1) Is where I am a solution?
2) No.  Ok, where can I go from here?  If I can go somewhere, choose a place to go.
3) Go there.
5) Was that a solution?  If yes, return true!
5) If there are remaining places to go, choose one and goto #3.
6) Out of places to go.  Return false.

(*) #3 is the recursive step.  At that point you are in a new place, and you start from #1 again

Imagine that you went to the library looking for a book, but oddly didn't know how to look one up using the alphabet or the Dewey decimal system.  What would be a good way of systematically finding the book?

Imagine that the stacks look like this:
---> x
[===| |===]
[===| |===]
[===| |===]
[===| |===]


Where [ and ] indicate dead ends, = indicates a shelf of books and | | indicates a central aisle between the books.  I've chosen an arbitrarily small portion of the library to illustrate the algorithm, but you may realize that's irrelevant later on.

So, you're at the x.  How to proceed?

One way is to step into the aisle and see where you can go from here:

[===|x|===]
[===| |===]
[===| |===]
[===| |===]


You can go right, left or forward.  So, let's try right:

[===| |x==]
[===| |===]
[===| |===]
[===| |===]


No luck...

Where can you go from here?  Because you just came from backward, you can only go forward.  So, do that.  Now, notice that I've marked where we just were with a '#' because there are no more places to go from there.  You might need to do this in code, you might not.  It depends on your algorithm.  The meaning of # in this case is just that there are no unexplored exits from that point in the library.

[===| |#x=]
[===| |===]
[===| |===]
[===| |===]


No luck...

Where can you go from here?  Because you just came from backward, you can only go forward.

[===| |##x]
[===| |===]
[===| |===]
[===| |===]


Man.  No luck here either.  On top of that, you ran out of places to go, because there's a wall in front of you.  So, back up until you have another place to go:

[===| |#x#]
[===| |===]
[===| |===]
[===| |===]

Nope...

[===| |x##]
[===| |===]
[===| |===]
[===| |===]

Nope...

[===|x|###]
[===| |===]
[===| |===]
[===| |===]


Ah, ok, from here, you can go right, or forward.  Let's go forward.  What we just did was the backtracking portion of the algorithm.

[===| |###]
[===|x|===]
[===| |===]
[===| |===]


From here, you can go left, right or forward.  If you repeat this process, you will examine every shelve in the library.  Let's imagine that we know where the book is:

[===| |###]
[===|x|===]
[=B=| |===]
[===| |===]


If you always chose the same order of directions to try (and that's usually what you want to do), you will explore these areas before you find the book:

[===| |###]
[===| |###]
[=B#| |###]
[###|#|###]


At that point, you've 'solved' your book finding problem, and should return true or the book's location or whatever it is that you are trying to determine.  Now, imagine that the book isn't in this part of the library.  You will actually examine every section of the library and eventually return to where you started from.

[###|x|###]
[###|#|###]
[###|#|###]
[###|#|###]


At this point, you know that you didn't find it, so return false.

Make sense?  Here's a *pseudojava* implementation:

boolean findBook(string book, point coords)
{
    if (shelves.shelfAt(coords).contains(book))
    {
        return true;
    }
    else
    {
        while (shelves.hasExit())
        {
            // nextExit returns the coordinates of where the next exit
            // leads to and marks that exit as used up
            if (findBook(book, shelves.nextExit()))
            {
                // found it!
                return true;
            }
        }
    }
    // ran out of places to go.  this is a dead end.
    return false;
}


So, for further consideration:

floor 1:
[===| |===]
[===| |===]
[===| |===]
[===| |===]

floor 2:
[===| |===]
[===| |===]
[===| |===]
[===| |===]

floor 3:
[===| |===]
[===| |===]
[===| |===]
[===| |===]


1) How would you change the algorithm above to handle moving between multiple floors in your search?

2) How could the above algorithm be changed to find the number of possible phone numbers in a particular area code, excepting those that start with some set of disallowed numbers such as {555, 666, 911, 411, 211, 311}?  Pro-hint: what if we numbered all the shelves in some systematic way and then counted the number of shelves we examined?