汉诺塔问题描述
有三根柱子S,D,T。S柱上有N(N>=1)个圆盘,盘的尺寸由下到上依次变小。要求按以下规则将所有盘移至D杆:
1.每次只能移动一个圆盘;
2.大盘不能叠在小盘上面。
一、自顶向下,逐渐求精,函数调用
我们要将S杆上的N个圆盘移动至D杆上,首先我们可以考虑到先试着用移动两个圆盘或者三个圆盘的简单情况来分析。通过分析后发现,要将N个圆盘从S杆移动到D杆,实质上就是先将S杆上的N-1个圆盘以D杆为辅助移动至T杆,然后将第N个圆盘直接移动至D杆,最后再将T杆上的N-1个圆盘以S杆为辅助移动至D杆,这样,我们就解决了这个问题。因此我们可以设计一个函数hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)来表示此过程,其中paraN表示圆盘的个数,paraSource是起始杆,paraDestination是目标杆,paraTransit是中间杆。
二、递归与分治
通过第一步的分析,当我们以D杆为中介时,将S杆上N-1个圆盘都移到杆T杆上,本身就为一个n-1的汉诺塔问题了,此时的起始杆是S杆,目标杆为T杆,再将S杆上第N个圆盘移动到D上;同样,以S杆为中介,将T杆上的N-1个圆盘移动到D杆上时,其本身也就为一个n-1的汉诺塔问题了,此时的起始杆为T杆,目标杆为D杆。因此,我们可以采用递归的思想来实现代码的过程,代码如下:
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
{
if (paraN <= 0)
{
return;
}
else
{
hanoi(paraN - 1, paraSource, paraTransit, paraDestination);
printf("The address is %d %d %d %d\r\n", ¶N, ¶Source, ¶Transit, ¶Destination); //打印地址
printf("The paraSource is %c,the paraDestination is %c,the paraTransit is %c\r\n", paraSource, paraDestination, paraTransit);
printf("%c -> %c \r\n", paraSource, paraDestination);
hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
}
}
该函数首先检查N的值,如果N为零则直接结束运行,如果N>=1,则该函数以递归的方式调用hanoi()函数来解决问题。
三、不要跨层分析
当移动N-1个圆盘时,我们不要逐个逐个地去考虑其中某一个圆盘是怎么移动的,其本质上还是将N-1个圆盘从起始杆移动至目标杆。因此,我们只需要认为这是一个N-1的汉诺塔问题,再次调用hanoi()函数,计算机会为我们解决此过程。
四、形参与实参
在函数调用过程中,我们使用形参来表示函数的输入参数,而实参则是在函数调用时提供的具体数值。在汉诺塔问题中,形参paraN表示圆盘的个数,paraSource表示起始杆,paraDestination表示目标杆,paraTransit表示中间杆。在我们调用函数时,我们需要提供实践的圆盘个数和三个杆的具体名称,如hanoi(3 , 'S' , 'D' , 'T')。
五、有意义规范的标识符
在编写程序时,使用有意义、规范的标识符可以使代码更易于理解和维护。在汉诺塔问题中,形参变量名paraN、paraSource、paraDestination、paraTransmit和函数名hanoi,我们可以通过英文意思清晰了解到它们每一个的作用,这些标识符可以更好地表达程序的含义,并提高代码的可读性。
六、时间复杂度
当 n=1时,移动步骤是1步,n=2时,移动步骤是3步,n=3时,移动步骤是7步,通过归纳法我们可以得到当有n个盘子时,移动次数时2^n-1,所以时间复杂度是O(2^n).。
七、递归栈
由于计算机在执行指令时,一次只能执行一条,所以计算机在执行指令时运用了栈结构。先进栈的指令被压在栈最底部执行,后读取的指令依次入栈,当读取到执行指令时再执行栈顶的指令。所以当调用hanoi函数时,递归函数将前面的所有调用全部压栈,当最底层调用结束后,开始读取执行指令,从栈顶指令开始执行,将结果返回下一层函数。
八、空间复杂度
我们以paraN=3为例
第一步:调用hanoi(3),在hanoi(3)中调用hanoi(2),以此类推
第二步:调用hanoi(0)直接返回,hanoi(1),hanoi(2)逐个返回
第三步:hanoi(3)第一部分完成,输出后进入第三部分,重复第一步、第二步,直至hanoi(3)结束
通过分析可以发现,当最后一条指令进栈执行后又会出栈,当n=3时,系统只给我们分配了三片空间,因此空间复杂度为O(n)。
整体代码
#include<stdio.h>
/*
* Hanoi.
*/
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
{
if (paraN <= 0)
{
return;
}
else
{
hanoi(paraN - 1, paraSource, paraTransit, paraDestination);
printf("The address is %d %d %d %d\r\n", ¶N, ¶Source, ¶Transit, ¶Destination); //打印地址
printf("The paraSource is %c,the paraDestination is %c,the paraTransit is %c\r\n", paraSource, paraDestination, paraTransit);
printf("%c -> %c \r\n", paraSource, paraDestination);
hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
}
}
/*
* Test the hanoi function.
*/
void hanoiTest()
{
printf("----addToTest begins.----\r\n");
printf("2 plates\r\n");
hanoi(2, 'A', 'B', 'C');
printf("3 plates\r\n");
hanoi(3, 'A', 'B', 'C');
printf("----addTotest ends.----\r\n");
}
/*
* The entrance.
*/
void main()
{
hanoiTest();
}
运行结果