汉诺塔问题:相传在古印度圣庙中,有一种被称为汉诺塔的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘。
游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。
操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
问题分析:
1.自顶向下,逐渐求精:
移动两个盘子时将第一个盘子移动至B杆,再将第二个盘子移动至C杆,最后将第一个盘子移动至C杆。移动三个盘子时可看作将两个盘子移动至B杆,再将第三个盘子移动至C杆,最后将两个盘子移动至C杆。因此可将问题转变为将n-1个盘子移动到B杆上,再把最后一个盘子移动到C杆上,最后再把剩下的n-1个盘子移动到C杆上。
2.形参与实参:
形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。在进行函数调用时,实参提供具体数值。在汉诺塔问题中,表示需移动的圆盘数量n和三个柱子a,b,c为形参,调用时需提供具体名称。
3.有意义、规范的标识符:
使用有意义、规范的标识符可以时代码易于理解和维护,能够提高代码的可读性。
4.时间复杂度:
时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数. 时间复杂度常用O表述,不包括这个函数的低阶项和首项系数。汉诺塔问题中,因为每次递归需移动两个盘子,所以时间复杂度为O(2^n)。
5.递归栈:
递归算法是一种直接或者间接调用自身函数或者方法的算法。递归的目的通常是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归栈可以在递归函数调用过程中保存函数调用状态。
6.空间复杂度:
空间复杂度是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 ,也就是额外占取的空间的大小。空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用O渐进表示法。汉诺塔问题中,递归栈的深度等于递归调用的层数,而需移动的盘子数量表示为n,所以空间复杂度为O(n)。
对于汉诺塔问题,以三个盘子为例,移动方式如下图所示:
由此可将移动方式看做将第一、二个盘子视为整体,把两个盘子移动至B杆,再将第三个盘子移动至C杆,最后将两个盘子移动至C杆。
得出:假设有n个盘子需要移动
1、把n-1个盘子移动到B杆上;
2、再把最后一个盘子移动到C杆上;
3、最后再把剩下的n-1个盘子移动到C杆上。
代码如下:
#include <stdio.h>
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit) {
printf("paraN is %d, paraN address is %d, paraSource address is %d, paraDestination address is %d, paraTransit address is %d\r\n",paraN, ¶N, ¶Source, ¶Destination, ¶Transit);
if (paraN <= 0) {
return;
} else {
hanoi(paraN - 1, paraSource, paraTransit, paraDestination);
printf("%c -> %c \r\n", paraSource, paraDestination);
hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
}
}
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");
}
int main() {
hanoiTest();
}
运行结果: