问题介绍及背景
汉诺塔,又称河内塔。是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上按照大小顺序摞着64片黄金圆盘,大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
接下来我们就分析一下汉诺塔问题的具体思路!
图解汉诺塔移动
n=3
这里可以理解为我们先将前n-1个圆盘借助C柱移到B柱,然后把最大的圆盘移到C柱,然后再以同样思路执行。
n=4
当n=4的时候,我们仍然可以先将前n-1个圆盘(这里即前三个圆盘)设法置于B柱(后面会讲具体操作),然后再将最大的圆盘置于C柱。如此操作后,将最大的圆盘视作固定便得到了和n=3时相似的情况,如上图。之后的移法便和n=3相同。
问题剖析及代码实现
前n-1个圆盘移动方法
前提:有n个圆盘以从小到大的顺序排在A柱上,有三个柱子,我们分别将这三个柱子记为A,B,C。
1.n为偶数时,按A->B->C->A的顺序移最小的圆盘,移一次;n为奇数时,按A->C->B->A的顺序挪移最小的圆盘,移一次;
2.接着,把另外两根柱子上可以移动的圆盘移到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘,移一次。这一步虽没有明确规定移动哪个圆盘,但执行的行动却是唯一的。
3.反复1和2操作,最后就完成了汉诺塔的移动。
移动次数
阶数 | 移动次数 | 规律 |
1 | 1 | 2^1-1 |
2 | 3 | 2^2–1 |
3 | 7 | 2^3-1 |
4 | 15 | 2^4-1 |
… | … | … |
n | \ | 2^n-1 |
如果我们将移动前n-1个圆盘视为Hanoi(n-1)
,根据上述结论,则移动n个圆盘次数可以用Hanoi(n)=2*Hanoi(n-1)-1
(n>0)计算。
源代码
int Hanoi(int n) { if (n > 0) return 2 * Hanoi(n - 1); else return 1; } int main() { int n = 0; printf("圆盘的个数:>"); scanf("%d", &n); int ret = Hanoi(n) - 1; printf("%d\n", ret); return 0; }
移动步骤打印
想要用代码打印出移动步骤,我们必须搞清楚整个过程。事实上汉诺塔移动有一个循环:n为偶数时,他总是以A->B,A->C,B->C,A->B,C->A,C->B循环;n为奇数时,他总是以A->C,A->B,C->B,A->C,B->A,B->C循环。
具体代码实现如下
#include<stdio.h> #include<windows.h> int count; void Move(int n, char a, char b) { count++; Sleep(1000); printf("第%2d次移动 Move %d: %c -> %c \n", count, n, a, b); } void Hanoi(int n, char a, char b, char c) { if (n == 1) { Move(n, a, c); } else { Hanoi(n - 1, a, c, b); Move(n, a, c); Hanoi(n - 1, b, a, c); } } int main() { int n = 0; printf("汉诺塔的层数:>"); scanf(" %d", &n); Hanoi(n, 'A', 'B', 'C'); return 0; }