#include <stdio.h>
void move(char A, int n, char C)
{
printf("%d,%c,%c\n", n, A, C);
}
void Hanoi(int n, char A, char B, char C)\\将n个圆盘从A柱移至C柱
{
if (n == 1)
move(A, 1, C);
else
{
Hanoi(n - 1, A, C, B);\\将n-1个圆盘从A移至B,C为工具柱
printf("The first A,B,C address is %ld %ld %ld\n", &A, &B, &C);
move(A, n, C);\\将序号为n的圆盘从A移至C
Hanoi(n - 1, B, A, C);\\将n-1个圆盘从B移至C,A为工具柱
printf("The next A,B,C address is %ld %ld %ld\n", &A, &B, &C);
}
}
int main()
{
printf("The first test.\n");
Hanoi(2, 'A', 'B', 'C');
printf("The next test.\n");
Hanoi(3, 'A', 'B', 'C');
return 0;
}
想成整体问题,1.先移n-1,2.再移n,3.最后将n-1移到n上
自上向下:逐渐求精,函数调用,把大问题分解成小问题。
递归与分治:基础与归纳,把原问题分解成子问题,但仍与原来一样。
要跨层分析:向下考虑。
递归实现汉诺塔问题(Hanoi):
定义了一个 hanoi() 函数,该函数有四个参数:将要移动的盘子数 paraN,源柱子 paraSource,目标柱子 paraDestination 和辅助移动的柱子 paraTransit。
如果盘子数为零,直接返回;
否则,先将 paraN-1 个盘子从源柱子移到辅助移动的柱子;
将第 paraN 个盘子从源柱子移到目标柱子,然后再将 paraN-1 个盘子从辅助移动的柱子移到目标柱子。
在移动每一个盘子时,都会打印一条提示信息,说明将它从哪个柱子移到哪个柱子printf("%c -> %c\r\n", paraSource, paraDestination);
分析
这个问题可以分解为3个子问题,即将 n-1 个圆盘从源柱子移动到辅助移动的柱子,将第 n 个圆盘从源柱子移动到目标柱子,再将 n-1 个圆盘从辅助移动的柱子移动到目标柱子。由此不断向下细分下去。
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
检查n的值:
如果n=1时,将圆盘直接从源柱子移动到目标柱子,
否则分为3个子问题,递归调用该函数
3.形参与实参
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
上面的函数里的参数都是形参,调用完了以后会释放。
实参是在调用时提供的具体数值
4.有意义、规范的标识符
使用有意义、规范的标识符可以使代码可读性更高,意义更加明确,更加专业。也是日后作为程序员的基本素养。
5.时间复杂度
移动次数表达式为2的n次方-1,时间复杂度也是指数级别的。这也是递归的一个缺点,即在调用层数过多时,时间复杂度会很高。
6.递归栈
递归和栈有着密不可分的关系。
在递归调用的过程中,每次调用都会将函数的参数、局部变量、返回地址等信息压入push函数调用栈中,当递归调用结束时,又会从栈中弹出pop这些信息,恢复到之前的调用状态。
递归是栈的一种应用场景,它常常被用于解决具有递归结构的问题。
7.空间复杂度
在递归调用的过程中,每次调用都会将函数的参数、局部变量、返回地址等信息压入push函数调用栈中,当递归调用结束时,又会从栈中弹出pop这些信息,所以递归栈的深度等于递归调用的层数,因此空间复杂度为 O(n)