今天下午,在昏昏欲睡的情况下外加中间补了十分钟的觉终于把这个汉诺塔的递归问题给弄明白了,下面进入正题:
据说,古印度有这样一个传说,大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
这就是经典的汉诺塔问题,那么,如何用编程语言去递归实现呢?
递归的思路如下:要把一根柱子上的N个盘子移到右边,首先把N-1个盘子移到左边(其实是最右边),然后把第N个盘子移动到右边,然后再把N-1个盘子移动到右边盘子N之上
代码如下:
void Hanoi(int N, int d) {
if(N == 0) {
return;
}
//+表示把当前标号为N的盘子,放到右边的柱子上,如果是在最右边的柱子,就移动到最左边(可以理解为这三个柱子是无限循环的,是个圈圈)
//-表示把当前标号为N的盘子,放到左边的柱子上,如果是在最左边的柱子,就移动到最右边
Hanoi(N - 1, -d);
Move(N, d);
Hanoi(N - 1, -d);
}
Move函数是移动盘子的函数,我只是了解递归如何实现汉诺塔,没有去具体实现,注意d是为1的,然后看下图:
为什么我只用了三个盘子,因为三个盘子最好理解。
三个盘子的函数调用如下:
Hanoi(2, -1);
Move(3, 1);
Hanoi(2, -1);
这就是三个盘子的函数调用,出现了两次Hanoi(2, -1),在看这个函数之前,先看一下Hanoi(2,1)这个情况,这种情况是,只有两个盘子的情况,如下图:
注意Hanoi(1, 1)函数只有一个操作就是Move(1, 1),也就是把一个盘子右移一个柱子,看这幅图,第一次把最上面的盘子移动到最右边了,(注意:-1虽然是左移但是这个盘子在最左边,所以就移动到最右边了),然后第二次,把最下面的盘子,移动到右边的柱子上了,然后再把最右边的1号盘子,左移一个柱子,这样,就把两个盘子的汉诺塔移动完成了。
所以,Hanoi(2, 1)这个函数是,把当前柱子上的两个盘子,右移到右边的柱子上,那么,Hanoi(2, -1)不就是把当前柱子上的两个盘子,移动到左边的柱子上(即最右边的柱子)。
那么,回到上面Hanoi(3, 1)的操作, 如图,把上面两个盘子移动到最右边的柱子,然后再把最底层的盘子移动到右边的柱子,再把位于最右边柱子上的两个函数,给左移回来,最后这个汉诺塔的移动完成了
要理解递归思想的话,不要一步一步去跟踪函数如何实现,那样会让人太混乱了,只需要理解递归,层与层之间的如何交接和最底层的思路
理解了这个思路,那么十层的汉诺塔,不就是这三个操作:
Hanoi(9, -1);
Move(10, 1);
Hanoi(9, -1);
把前九层移动到最右边,然后再把最后一层的盘子移动到右边的柱子上,然后再把前九层的盘子左移一个柱子,因为是三个柱子所以刚好就完成了,至于前九层的盘子怎么移动,当然是跟这个思路一样了。
这种方法是递归分治算法产生一个2^N - 1步移动的算法
我贴一个链接:如何理解汉诺塔的递归? - Fireman A的回答 - 知乎
https://www.zhihu.com/question/24385418/answer/257751077
这个回答很不错,讲了一个故事,很有趣味性又让人一下子就明白了汉诺塔的递归