Skip to content
Home DSA Tower of Hanoi Explained: Recursion, Logic & Real Code

Tower of Hanoi Explained: Recursion, Logic & Real Code

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Recursion → Topic 3 of 9
Tower of Hanoi recursion explained deeply — why it works, how to code it in Java, common mistakes, and interview questions you'll actually face.
⚙️ Intermediate — basic DSA knowledge assumed
In this tutorial, you'll learn
Tower of Hanoi recursion explained deeply — why it works, how to code it in Java, common mistakes, and interview questions you'll actually face.
  • The recursive solution works because the problem has self-similar structure — moving N discs always reduces to moving N-1 discs twice with one single move in between.
  • The base case is discCount == 1, not 0 — getting this wrong either causes StackOverflowError or silently skips single-disc moves, giving an incorrect move count.
  • The minimum number of moves is always exactly 2ⁿ − 1 — this is provable from the recurrence T(n) = 2T(n-1) + 1, and no algorithm can solve it in fewer moves.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer

Imagine you're moving house, but your van only fits one box at a time, and you're not allowed to stack a big box on top of a small one. You have three spots to park boxes — your old place, your new place, and a friend's driveway as a temporary stop. Tower of Hanoi is exactly that puzzle: move a stack of discs from peg A to peg C, one disc at a time, never placing a bigger disc on a smaller one. The magic is that solving it for N discs always means first solving it for N-1 discs — which is exactly what recursion is built for.

Most recursion tutorials give you factorial or Fibonacci and call it a day. Tower of Hanoi is different — it's the puzzle that makes recursion click. It's used in computer science courses worldwide not because it's academically cute, but because it perfectly mirrors how recursive thinking actually works: break a big problem into a smaller version of itself, trust the process, and let the call stack do the heavy lifting. It shows up in coding interviews at companies like Google, Amazon, and Meta — not because they want you to memorise it, but because solving it live reveals whether you can think recursively under pressure.

The problem has a deceptively simple rule set: three pegs, N discs stacked from largest to smallest on the first peg, and you need to move the whole stack to the last peg. You can only move one disc at a time, and a larger disc can never sit on top of a smaller one. An iterative solution to this problem is nightmarishly complex. A recursive solution is about eight lines of code. That contrast is the entire point — recursion isn't just a technique, it's the right tool for problems with self-similar structure.

By the end of this article you'll have a mental toolkit of those patterns: DFS vs BFS trade-offs, the two-pointer trick adapted for trees, the post-order 'gather-then-decide' approach, and more. Every problem below is chosen because it directly teaches a transferable pattern. Work through the code, tweak the inputs, break it — that's how the pattern becomes muscle memory.

Why Recursion Is the Only Sane Approach Here

Before writing a single line of code, let's understand why recursion is the natural fit and not just a clever trick.

The key insight is this: to move N discs from peg A to peg C, you must first move the top N-1 discs out of the way (to peg B), then slide the biggest disc to peg C, then move the N-1 discs from peg B onto peg C. That's it. That's the whole algorithm.

Notice that moving N-1 discs is exactly the same problem — just smaller. And moving N-2 discs is the same problem, even smaller. This self-similar structure is the definition of a recursively solvable problem. Every recursive call is trusting a slightly smaller version of itself to just work.

The base case is when N equals 1. You don't need to move anything out of the way — you just pick up the single disc and place it directly on the destination peg. That's your stopping condition. Without it, the function calls itself forever and your stack overflows.

io/thecodeforge/recursion/TowerOfHanoi.java · JAVA
1234567891011121314151617181920212223242526272829303132333435
package io.thecodeforge.recursion;

public class TowerOfHanoi {

    /**
     * io.thecodeforge: Standard recursive implementation of Tower of Hanoi.
     * Complexity: O(2^n)
     */
    public static void moveDiscs(
            int discCount,
            char sourcePeg,
            char destinationPeg,
            char auxiliaryPeg) {

        // BASE CASE: only one disc left — move it directly
        if (discCount == 1) {
            System.out.println("Move disc 1 from peg " + sourcePeg + " to peg " + destinationPeg);
            return;
        }

        // STEP 1: Move top (n-1) discs to auxiliary to free up the biggest disc
        moveDiscs(discCount - 1, sourcePeg, auxiliaryPeg, destinationPeg);

        // STEP 2: Move the largest remaining disc to destination
        System.out.println("Move disc " + discCount + " from peg " + sourcePeg + " to peg " + destinationPeg);

        // STEP 3: Move (n-1) discs from auxiliary to destination
        moveDiscs(discCount - 1, auxiliaryPeg, destinationPeg, sourcePeg);
    }

    public static void main(String[] args) {
        int n = 3;
        moveDiscs(n, 'A', 'C', 'B');
    }
}
▶ Output
Move disc 1 from peg A to peg C
Move disc 2 from peg A to peg B
Move disc 1 from peg C to peg B
Move disc 3 from peg A to peg C
Move disc 1 from peg B to peg A
Move disc 2 from peg B to peg C
Move disc 1 from peg A to peg C
💡Pro Tip:
Notice how the peg labels rotate between the three recursive roles — source, destination, auxiliary — in each call. If you get confused tracing it, label them by ROLE, not by letter. The letter is just a name; the role is what matters.

Tracing the Call Stack: What Actually Happens Step by Step

Understanding the code is one thing. Understanding what the call stack looks like is what separates someone who memorised the solution from someone who truly gets recursion.

Let's trace N=3. The first call is moveDiscs(3, A, C, B). Before printing anything, it immediately calls moveDiscs(2, A, B, C). Before that prints anything, it calls moveDiscs(1, A, C, B) — which hits the base case and prints 'Move disc 1 from A to C'. Then control returns to the N=2 frame, which prints 'Move disc 2 from A to B', then calls moveDiscs(1, C, B, A), printing 'Move disc 1 from C to B'.

This is the critical mental model: recursive calls don't all run at once. Each frame is paused — frozen mid-execution — while it waits for a deeper call to return. The call stack is literally a stack of paused function frames. When a base case fires, the stack starts unwinding.

io/thecodeforge/recursion/TowerOfHanoiWithDepth.java · JAVA
12345678910111213141516
package io.thecodeforge.recursion;

public class TowerOfHanoiWithDepth {
    public static void moveWithTrace(int n, char s, char d, char a, int depth) {
        String indent = "  ".repeat(depth);
        if (n == 1) {
            System.out.println(indent + "[Base Case] Disc 1: " + s + " -> " + d);
            return;
        }
        
        System.out.println(indent + "Pushing frame N=" + n);
        moveWithTrace(n - 1, s, a, d, depth + 1);
        System.out.println(indent + "Current N=" + n + ": " + s + " -> " + d);
        moveWithTrace(n - 1, a, d, s, depth + 1);
    }
}
▶ Output
Visualizes the stack growing and shrinking with each recursive branch.
🔥Interview Gold:
When an interviewer says 'trace through this for N=3', use this indented format on the whiteboard. It shows you understand the call stack, not just the output. That distinction gets you the offer.

The Math Behind the Minimum Moves: Why 2ⁿ − 1?

Every correct solution to Tower of Hanoi with N discs takes exactly $2^n - 1$ moves. You can't do it in fewer. This isn't a fun fact — it's a provable consequence of the algorithm's structure.

Let's define $T(n)$ as the minimum number of moves needed for N discs. For $N=1$, that's 1 move. For any $N > 1$, the algorithm does $T(n-1)$ moves to shift the top stack to auxiliary, then 1 move for the big disc, then $T(n-1)$ moves again to shift the small stack onto the destination.

Formula: $T(n) = 2 imes T(n-1) + 1$. Unrolling this recurrence yields the geometric series result: $T(n) = 2^n - 1$.

io/thecodeforge/recursion/HanoiMath.java · JAVA
123456789101112
package io.thecodeforge.recursion;

public class HanoiMath {
    public static long calculateMinMoves(int n) {
        // Use long to prevent overflow for N > 30
        return (long) Math.pow(2, n) - 1;
    }
    
    public static void main(String[] args) {
        System.out.println("Moves for 64 discs: " + calculateMinMoves(64));
    }
}
▶ Output
Total moves for N=64 is 18,446,744,073,709,551,615.
⚠ Watch Out:
Use long, not int, when computing 2ⁿ − 1 for large N. For N=32, (int) Math.pow(2, 32) - 1 overflows silently and gives you -1. Always cast to long or use BigInteger for anything above N=30.

Storing Move History: A Practical Real-World Extension

Printing to the console is fine for learning, but in a real application — say, building a game, an animation engine, or a puzzle validator — you need to capture each move as structured data you can process, store, or replay. This transition uses the 'accumulator pattern,' threading a List through the recursive calls to collect results without breaking the functional purity of the logic.

io/thecodeforge/recursion/HanoiMoveRecorder.java · JAVA
1234567891011121314151617
package io.thecodeforge.recursion;

import java.util.*;

public class HanoiMoveRecorder {
    public record Move(int disc, char from, char to) {}

    public static void record(int n, char s, char d, char a, List<Move> history) {
        if (n == 1) {
            history.add(new Move(1, s, d));
            return;
        }
        record(n - 1, s, a, d, history);
        history.add(new Move(n, s, d));
        record(n - 1, a, d, s, history);
    }
}
▶ Output
Returns a structured list of Move objects for post-processing.
💡Pro Tip:
Passing a mutable list into a recursive function (the 'accumulator pattern') is cleaner than returning a list and merging results at every call frame. It avoids creating a new List per recursive call and keeps memory usage at O(2ⁿ) for the list rather than adding O(n) overhead per call frame for merging.
AspectRecursive SolutionIterative Solution
Code length~10 lines of logic40-60 lines with manual stack simulation
ReadabilityMaps directly to the problem's logicHard to follow — simulates what the OS does for you
Call stack usageO(n) stack frames at any one timeO(n) explicit stack objects on the heap
PerformanceSame number of moves: 2ⁿ − 1Same number of moves: 2ⁿ − 1
Risk of stack overflowYes, for very large N (N > ~5000 in Java)No — uses heap instead of call stack
Interview preferenceAlmost always expected recursively firstSometimes asked as a follow-up challenge
TestabilityEasy — pure function with predictable outputHarder — more state to track and verify
Best used whenN is small to moderate (educational, games, puzzles)N is very large and stack depth is a concern

🎯 Key Takeaways

  • The recursive solution works because the problem has self-similar structure — moving N discs always reduces to moving N-1 discs twice with one single move in between.
  • The base case is discCount == 1, not 0 — getting this wrong either causes StackOverflowError or silently skips single-disc moves, giving an incorrect move count.
  • The minimum number of moves is always exactly 2ⁿ − 1 — this is provable from the recurrence T(n) = 2T(n-1) + 1, and no algorithm can solve it in fewer moves.
  • Replacing print statements with a move list (the accumulator pattern) is the real-world version of this algorithm — it makes the output testable, storable, and replayable without touching the recursive logic.

⚠ Common Mistakes to Avoid

    Swapping auxiliaryPeg and destinationPeg in the recursive calls
    Symptom

    the output looks almost right but some discs end up on the wrong peg, often a violation of the 'no large on small' rule —

    Fix

    remember the role rotation pattern: (source → destination), then the next call swaps destination and auxiliary. Write it out in English first before coding: 'move N-1 to the spare, move the big one to the goal, move N-1 from the spare to the goal'.

    Missing or wrong base case
    Symptom

    StackOverflowError, or the function terminates early and prints nothing for N=1 —

    Fix

    the base case must be discCount == 1 (not 0). If you use 0, you'll move a phantom disc. If you use discCount <= 0 without a print statement, the single-disc case silently does nothing and your move count will be wrong.

    Using int instead of long for move counts at large N
    Symptom

    for N >= 32, countMoves() returns a negative number or overflows silently —

    Fix

    the return type and any accumulator variables must be long. For N=32, 2^32 - 1 is 4,294,967,295 — well beyond Integer.MAX_VALUE of 2,147,483,647. Switch to long or BigInteger immediately when scaling up.

Interview Questions on This Topic

  • QWhat is the time complexity of the Tower of Hanoi algorithm? Explain why it is exponential.
  • QCan you derive the minimum number of moves formula $2^n - 1$ using the recurrence relation?
  • QHow would you implement this iteratively? Is there any benefit to an iterative approach in a production environment?
  • QTower of Hanoi has O(2ⁿ) time complexity — if an interviewer asks you to make it faster, how do you respond? (The answer: you can't. 2ⁿ − 1 moves is provably optimal. The right answer is to explain WHY, not scramble for an optimisation that doesn't exist.)

Frequently Asked Questions

What is the time complexity of Tower of Hanoi and why?

The time complexity is $O(2^n)$. This occurs because each call to move $n$ discs triggers two calls to move $n-1$ discs. This doubling effect at every level of the recursion tree results in an exponential growth of operations ($2^n - 1$ moves total).

Can Tower of Hanoi be solved without recursion?

Yes, it can be solved iteratively using a bitwise approach or by manually managing a stack. However, the recursive approach is generally preferred for its elegance and direct mapping to the problem's mathematical structure.

Why is the base case $n=1$ and not $n=0$?

While $n=0$ is technically a valid base case (meaning 'do nothing'), $n=1$ is the smallest meaningful unit of the puzzle. Using $n=1$ allows you to print the final move directly within the base case block, keeping the code concise and avoiding unnecessary empty function calls.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousRecursion vs IterationNext →Divide and Conquer Technique
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged