问题:设有3根编号分别为A、B、C的柱子,在A柱上有n个盘子,每一个盘子都比它下面的一个盘子小一些,最下面的盘子是最大的,若要把A上的盘子全部按以下要求移到C上,则应该按什么顺序移动?
要求:
1、一次只能移动一个盘子
2、移动过程中大盘子不能放在小盘子上面
3、在移动过程中盘子可以放在A、B、C的任意一个上面。
思路:
我们先考虑最简单的情况,若A上只有一个盘子,则我们只需要将它移动到C上即可。
A-->C
若A上有两个盘子,我们就需要借助B的帮助了,将B作为中转站,先将小盘子放在B上,再将大盘子放在C上,最后将B上的小盘子放在C上。
A-->B
A-->C
B-->C
若有3个盘子,还是首先借助B,将前两个小盘子放在B上,再将最大的盘子放在C上,然后再借助A将两个小盘子依次按要求放在C上。
若有4个盘子,我们也可以按照上面的思路进行
五阶汉诺塔问题
因此我们得到了问题的解法:
当n=1时,只要从A移动到C即可;
当n>1时,此时需要借助另一个柱子的帮助来移动。我们将移动的步骤分解一下:
1、将A上的n-1个盘子借助C移动到B上;
2、将A上第n个盘子(即在移动完后剩下的那一个盘子)移动到C上;
3、最后将B上剩余的n-1个盘子借助A盘移动到C上。
至此,我们解决了汉诺塔问题中该如何摆放的问题。
汉诺塔问题的递归体(程序中自相似的部分):
移动n-1个盘子和移动n个盘子的过程是相似的,但整个过程又不完全一样,因为盘子摆放的柱子变了。
汉诺塔问题的递归出口(递归终止语句):
n=1的状态。
下面我们从递归和栈的角度去理解汉诺塔问题中的递归过程
我们以三阶汉诺塔问题为例
在栈中即为:
(6行,2,‘B’,‘A’,‘C’)
(4行,2,‘B’,‘A’,‘C’)
(6行,3,‘A’,‘B’,‘C’)
(6行,2,‘A’,‘C’,‘B’)
(4行,2,‘A’,‘C’,‘B’)
(4行,3,‘A’,‘B’,‘C’)
程序:
void move(char a, char b)//移动函数,表示将a上的一个盘子移动到b上
{
printf("%c-->%c", a, b);
}
void hanoi(int n, char x, char y, char z)//n为盘子的总数,x为需要移动的柱子,y为协助的中转的柱子,z为盘子最后转移到的柱子
{
if(n==1) move(x,z);//如果只有一个盘子,则直接将盘子转移到最后需要转移到达的柱子
else
{
hanoi(n-1, x, z, y);//先将n-1个盘子从x借助z转移到y上
move(x,z);//转移x上第n个盘子到z上
hanoi(n-1, y, x, z);//将y上的n-1个盘子借助x转移到z上
}
}
假设n为圆盘的数量,T(n)为移动n个圆盘的次数
当n=1时,T(1)=1;
当n=2时,T(2)=2T(1)+1=3;
当n=3时,T(3)=2T(2)+1=7;
…
故n阶汉诺塔问题最少共需要2^n-1步
算法的时间复杂度为:O(2^n)
参考文章:
汉诺塔(Hanoi)问题归纳总结_hanoi问题__坐看云起时_的博客-CSDN博客
【精选】汉诺塔(hanoi)问题从0到1详解_汉诺塔问题-CSDN博客