Tower of Hanoi
There are three pegs and n disks of different sizes stacked on the first peg, largest at the bottom. Move the whole stack to the third peg. You may move only one disk at a time, only the top disk of a peg, and you may never place a larger disk on top of a smaller one. Return (or print) the sequence of moves; the minimum number of moves is 2ⁿ − 1. This is the classic showcase of multiple recursion.
- 1 <= n <= 5 (kept small: the move count is 2ⁿ − 1)
- Exactly 3 pegs, usually named source, auxiliary, destination
- A larger disk may never rest on a smaller one
n = 11 moven = 23 movesn = 37 movesn = 415 movesTower of Hanoi seems to demand clever planning — how do you juggle n disks across three pegs without ever stacking big on small? Recursion dissolves the cleverness. The key is to stop thinking about individual disk moves and instead trust a smaller version of the very same task. This problem showcases multiple recursion: two recursive calls that are not overlapping subproblems (so no memoization) but genuinely different sub-tasks.
To move n disks from the source peg to the destination peg, the only real obstacle is the biggest disk at the bottom — it can only go to the destination when the destination is empty and all n − 1 disks above it are parked somewhere out of the way. That "somewhere" is the third (auxiliary) peg. So the entire task splits into three steps:
Steps 1 and 3 are the same problem with one fewer disk — just with the peg roles relabeled. That is the leap of faith: assume you can already solve Hanoi for n − 1 disks, and the n-disk solution is these three lines.
def hanoi(n, source, dest, aux):
if n == 0: # base case: no disks, nothing to do
return
hanoi(n - 1, source, aux, dest) # 1: park n-1 on the spare peg
move(source, dest) # 2: move the largest disk
hanoi(n - 1, aux, dest, source) # 3: bring n-1 onto the destinationThis is multiple recursion — two recursive calls per invocation, like Fibonacci. But there is a crucial difference: Fibonacci's two calls overlapped (both branches needed fib(k)), which is why caching helped. Hanoi's two calls solve disjoint sub-tasks — moving a specific set of disks between a specific pair of pegs — so nothing repeats and there is nothing to memoize. The work is genuinely 2ⁿ − 1 moves; that is the true minimum, not wasted recomputation. Counting the calls: each level doubles the number of sub-tasks, giving O(2ⁿ) total moves and O(n) stack depth (the recursion is at most n frames deep at any instant, even though it makes exponentially many moves overall).
Hanoi is the archetype for problems solved by describing the step, not the whole plan: define the task in terms of smaller instances of itself, handle the single pivotal action in the middle, and let recursion assemble the exponentially many moves. The same "do the sub-task, take one action, do the mirror sub-task" shape reappears across recursion and backtracking — generating permutations, exploring decision trees, solving puzzles. When a problem's full plan looks impossibly tangled, ask what one smaller version you could trust, and what single action connects two of them.