The Tower of Hanoi
In the Tower of Hanoi puzzle:
- There are three stacks (towers), numbered 0, 1, 2.
- There are N disks numbered 0, ..., N-1.
=> 0 is the smallest. - Initially, the disks are placed in tower 0, in the order largest to smallest.
=> Smallest on top. - Goal: to move the disks to tower 1 using only legal moves.
- What's a legal move?
- You can move only a disk at the top of a tower.
- You can move only one disk at a time.
- You cannot place a disk on top of a smaller one.
- There are three key steps in the recursion:
- In pseudocode (without base case):
Algorithm towerOfHanoi (n, i, j) Input: Disks numbered 0, ..., n are to be moved from tower i to tower j 1. // ... base case ... // First find the third tower, other than i and j: 2. k = otherTower (i, j) // Step 1: move disks 0,..,n-1 from i to k 3. towerOfHanoi (n-1, i, k) // Step 2: move disk# n from i to j 4. move (n, i, j) // Step 3: move disks 0,...,n-1 from k to j 5. towerOfHanoi (n-1, k, j)
- The base case: move a single disk
Here's the program: (source file)
public class TowerOfHanoi { public static void main(String[] argv) { // A 3-disk puzzle: System.out.println("3-Disk solution: "); solveHanoi(2, 0, 1); // A 4-disk puzzle: System.out.println("4-Disk solution: "); solveHanoi(3, 0, 1); } static void solveHanoi(int n, int i, int j) { // Bottom-out. if (n == 0) { // The smallest disk. move(0, i, j); return; } int k = other(i, j); solveHanoi(n - 1, i, k); // Step 1. move(n, i, j); // Step 2. solveHanoi(n - 1, k, j); // Step 3. } static void move(int n, int i, int j) { // For now, we'll merely print out the move. System.out.println("=> Move disk# " + n + " from tower " + i + " to tower " + j); } static int other(int i, int j) { // return 3-i-j; // this is simpler. // Given two towers, return the third. if ((i == 0) && (j == 1)) { return 2; } else if ((i == 1) && (j == 0)) { return 2; } else if ((i == 1) && (j == 2)) { return 0; } else if ((i == 2) && (j == 1)) { return 0; } else if ((i == 0) && (j == 2)) { return 1; } else if ((i == 2) && (j == 0)) { return 1; } // We shouldn't reach here. return -1; } }
Note:
- The above program merely prints out the moves needed
=> We don't actually maintain the state of each tower.
Next, suppose we want to maintain the state of each tower:
- The ideal data structure is a stack.
- We'll use one stack for each tower.
Here's the program: (source file)
import java.util.*; public class TowerOfHanoi2 { // towers[0], towers[1] and towers[2] are the three stacks. static Stack<Integer>[] towers; public static void main(String[] argv) { // A 3-disk puzzle: System.out.println("3-Disk solution: "); solveHanoi(2, 0, 1); // A 4-disk puzzle: System.out.println("4-Disk solution: "); solveHanoi(3, 0, 1); } static void solveHanoi(int n, int i, int j) { // Create the three stacks. towers = new Stack[3]; for (int k = 0; k < 3; k++) { towers[k] = new Stack<Integer>(); } // Put disks 0,...,n on stack 0. for (int k = n; k >= 0; k--) { towers[0].add(k); } // Print the initial stack. printTowers(); // Now solve recursively. Note: this is the method below. solveHanoiRecursive(n, i, j); } static void solveHanoiRecursive(int n, int i, int j) { // Bottom-out. if (n == 0) { move(0, i, j); return; } int k = other(i, j); solveHanoiRecursive(n - 1, i, k); // Step 1. move(n, i, j); // Step 2. solveHanoiRecursive(n - 1, k, j); // Step 3. } static void move(int n, int i, int j) { // Pull out the top disk on stack i. int topVal = towers[i].pop(); // Put it on stack j. towers[j].push(topVal); // Print status. System.out.println("Towers after moving " + n + " from tower " + i + " to tower " + j); printTowers(); } static int other(int i, int j) { return 3-i-j; } static void printTowers() { for (int i = 0; i < towers.length; i++) { System.out.print("Tower " + i + ": "); if (!towers[i].isEmpty()) { for (Integer I : towers[i]) { System.out.print(" " + I); } } System.out.println(); } } }
From: http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html