摘要:汉诺塔大家都知道怎么玩,就是n个圆盘从柱子A移到柱子C。在移动过程中,小盘一定要在大盘上面,所以用到起中转作用的柱子B。下面的代码中有三个解法,一个是递归的,另两个是非递归的。
稍微讲一下非递归的:
方法一、是由一位美国学者发现一种出人意料的方法,只要轮流进行两步操作就可以了。
首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上。根据圆盘的数量确定柱子的排放顺序:若n为偶数,按顺时针方向依次摆放 A B C;若n为奇数,按顺时针方向依次摆放 A C B。
(1)按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A。
(2)接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的。
(3)反复进行(1)(2)操作,最后就能按规定完成汉诺塔的移动。
方法二、通过二叉树
中序遍历这棵树就得到汉诺塔的操作过程:
1:one->three; => 2:one->two; => 1:three->two; => 3:one->three; =>1:two->one; => 2:two->three; => 1:one->three

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Hanoi 7 { 8 struct Disk 9 { 10 public const int MAX = 64; 11 12 int top; //栈顶,用来最上面的圆盘 13 char name; //柱子的名字,可以是A,B,C中的一个 14 15 int[] s; //柱子上的圆盘存储情况 16 17 public int[] S 18 { 19 get 20 { 21 //初始化 22 if (s == null) 23 s = new int[MAX]; 24 return s; 25 } 26 set 27 { 28 s = value; 29 } 30 } 31 32 public int Top 33 { 34 get { return top; } 35 set { top = value; } 36 } 37 38 public char Name 39 { 40 get { return name; } 41 set { name = value; } 42 } 43 44 public bool IsEmpty() 45 { 46 return top == -1; 47 } 48 public int Peek()//取栈顶元素 49 { 50 return s[top]; 51 } 52 public int Pop()//出栈 53 { 54 return s[top--]; 55 } 56 public void Push(int x)//入栈 57 { 58 s[++top] = x; 59 } 60 } 61 62 class Node 63 { 64 int number; 65 char first; 66 char second; 67 char third; 68 69 public int Number 70 { 71 get { return number; } 72 set { number = value; } 73 } 74 internal char First 75 { 76 get { return first; } 77 set { first = value; } 78 } 79 internal char Second 80 { 81 get { return second; } 82 set { second = value; } 83 } 84 internal char Third 85 { 86 get { return third; } 87 set { third = value; } 88 } 89 90 public Node GetLeft() 91 { 92 if (number == 1) 93 return null; 94 Node node = new Node(); 95 node.number=this.number-1; 96 node.first = this.first; 97 node.second = this.third; 98 node.third = this.second; 99 return node; 100 } 101 public Node GetRight() 102 { 103 if (number == 1) 104 return null; 105 Node node = new Node(); 106 node.number = this.number - 1; 107 node.first = this.second; 108 node.second = this.first; 109 node.third = this.third; 110 return node; 111 } 112 } 113 114 class Program 115 { 116 static void Main(string[] args) 117 { 118 Console.WriteLine("递归实现"); 119 Hannoi(4, 'A', 'B', 'C'); 120 121 Console.WriteLine("非递归实现1"); 122 int n = 4; 123 Disk[] ta = Creat(n); //给结构数组设置初值 124 long max = Pow(2, n) - 1;//移动的次数应等于2^n - 1 125 Hannuota(ta, max);//移动汉诺塔的主要函数 126 127 Console.WriteLine("非递归实现2"); 128 Hanoi(4, 'A', 'B', 'C'); 129 Console.Read(); 130 } 131 132 #region 递归实现 133 static void Hannoi(int n, char a, char b, char c) 134 { 135 if (n == 1) 136 Move(1, a, c); 137 else 138 { 139 Hannoi(n - 1, a, c, b); 140 Move(n, a, c); 141 Hannoi(n - 1, b, a, c); 142 } 143 } 144 static void Move(int n, char x, char y) 145 { 146 Console.WriteLine("Move disk {0} from {1} to {2}", n, x, y); 147 } 148 #endregion 149 150 #region 非递归实现1 151 static Disk[] Creat(int n) 152 { 153 Disk[] ta = new Disk[3]; 154 ta[0].Name = 'A'; 155 ta[0].Top = n - 1; 156 //把所有的圆盘按从大到小的顺序放在柱子A上 157 for (int i = 0; i < n; i++) 158 { 159 ta[0].S[i] = n - i; 160 } 161 //柱子B,C上开始没有没有圆盘 162 ta[1].Top = ta[2].Top = -1; 163 for (int i = 0; i < n; i++) 164 ta[1].S[i] = ta[2].S[i] = 0; 165 //若n为偶数,按顺时针方向依次摆放 A B C 166 if (n % 2 == 0) 167 { 168 ta[1].Name = 'B'; 169 ta[2].Name = 'C'; 170 } 171 else //若n为奇数,按顺时针方向依次摆放 A C B 172 { 173 ta[1].Name = 'C'; 174 ta[2].Name = 'B'; 175 } 176 return ta; 177 } 178 179 static long Pow(int x, int y) 180 { 181 long sum = 1; 182 for (int i = 0; i < y; i++) 183 sum *= x; 184 185 return sum; 186 } 187 188 /// <summary> 189 /// 算法介绍:首先容易证明,当盘子的个数为n时,移动的次数应等于2^n - 1。 190 /// 一位美国学者发现一种出人意料的方法,只要轮流进行两步操作就可以了。 191 /// 首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上。根据圆盘的数量确定柱子的排放顺序:若n为偶数,按顺时针方向依次摆放 A B C;若n为奇数,按顺时针方向依次摆放 A C B。 192 /// (1)按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A。 193 /// (2)接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的。 194 /// (3)反复进行(1)(2)操作,最后就能按规定完成汉诺塔的移动。 195 /// </summary> 196 /// <param name="ta"></param> 197 /// <param name="max"></param> 198 static void Hannuota(Disk[] ta, long max) 199 { 200 Console.WriteLine("累计移动次数:{0}", max); 201 int k = 0; //累计移动的次数 202 int i = 0; 203 int ch; 204 while (k < max) 205 { 206 //按顺时针方向把圆盘1从现在的柱子移动到下一根柱子 207 ch = ta[i % 3].Pop(); 208 ta[(i + 1) % 3].Push(ch); 209 ++k; 210 Console.WriteLine("Move dish {0} from {1} to {2}", ch, ta[i % 3].Name, ta[(i + 1) % 3].Name); 211 212 i++; 213 //把另外两根柱子上可以移动的圆盘移动到新的柱子上 214 if (k < max) 215 { //把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘 216 if (ta[(i + 1) % 3].IsEmpty() || 217 !ta[(i - 1) % 3].IsEmpty() && 218 ta[(i + 1) % 3].Peek() > ta[(i - 1) % 3].Peek()) 219 { 220 ch = ta[(i - 1) % 3].Pop(); 221 ta[(i + 1) % 3].Push(ch); 222 ++k; 223 Console.WriteLine("Move dish {0} from {1} to {2}", ch, ta[(i - 1) % 3].Name, ta[(i + 1) % 3].Name); 224 } 225 else 226 { 227 ch = ta[(i + 1) % 3].Pop(); 228 ta[(i - 1) % 3].Push(ch); 229 ++k; 230 Console.WriteLine("Move dish {0} from {1} to {2}", ch, ta[(i + 1) % 3].Name, ta[(i - 1) % 3].Name); 231 } 232 } 233 } 234 } 235 #endregion 236 237 #region 非递归实现2 238 static void Hanoi(int n,char a,char b,char c) 239 { 240 Stack<Node> s = new Stack<Node>(); 241 Node p=new Node(); 242 p.Number = n; 243 p.First = a; 244 p.Second = b; 245 p.Third = c; 246 int i = 0; 247 while (p != null || s.Count != 0) 248 { 249 while (p != null) 250 { 251 s.Push(p); 252 p = p.GetLeft(); 253 } 254 if (s.Count != 0) 255 { 256 p = s.Pop(); 257 i++; 258 Console.WriteLine("Move disk {0} from {1} to {2}", p.Number.ToString(), p.First.ToString(), p.Third.ToString()); 259 p = p.GetRight(); 260 } 261 } 262 Console.WriteLine("累计移动次数:{0}", i); 263 } 264 #endregion 265 } 266 }
网上找到汉诺塔操作可视化界面 http://figue.zdbit.com/cxzp.asp 。
Java递归简单实现

//汉诺塔 public class HanoiTest { public static void hanoi(int n, char A,char B,char C) { //何时结束,n=1(最后一块) if(n==1) { move(A,C); return; } //把n-1个从A->B hanoi(n-1,A,C,B); //最后1个充A->C move(A,C); //把n-1个从B->C hanoi(n-1,B,A,C); } public static void move(char point1,char point2) { System.out.println(String.format("%c -> %c",point1,point2)); } public static void main(String[] args) { hanoi(3,'A','B','C'); } }