#include <stdio.h>
/**
*递归处理汉诺塔
*/
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
{
if (paraN == 0) return;
else
{
hanoi(paraN - 1, paraSource, paraTransit, paraDestination);
printf("%c -> %c\n", paraSource, paraDestination);
printf("%p -> %p\n", ¶Source, ¶Destination);
hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
}
}
/**
*测试函数,使用两个例子进行测试。将三根柱子设为a,b,c。
*/
void hanoiTest()
{
printf("--- addToTest begins. ---\n");
printf("2 plates\n");
hanoi(2, 'A', 'B', 'C');
printf("3 plates\n");
hanoi(3, 'A', 'B', 'C');
printf("---- addToTest ends. ---\n");
}
void main()
{
hanoiTest();
}
计算机执行指令只能一条一条依次执行,因此运用栈结构,先进栈的指令被压在底层,后进栈的指令放在上层,指令遵循先进后出,后进先出的原则进行运行。当调运递归函数hanoi时,将前面的函数又一次压栈,底层栈的调用结束后,开始执行读取指令,从栈顶指令开始执行,将结果返回下一层函数。
1.自顶向下,逐渐求精,函数调用:汉诺塔问题的实现是运用了栈的原理,将函数指令依次压栈,自顶向下依次弹栈,依靠后进先出,先进后出的原则,逐步解决,运用了递归函数的思想进行函数调用,解决了这一问题。
2.递归与分治:递归只能以递归的思想来理解,通俗一点就是外包,假设有N个人,第N个人只需要做第N件事情,剩下的N-1件事情外包给第N-1个人来做,以此类推,每个人都只需要做自己当前所做的事,其他的事情外包给剩下的人做,至于他们怎么做,不关我的事,这就是分治。
3.不要跨层分析:在2.中已经大概分析过,递归分治的基本就是不要跨层分析,这一层的函数我们只需要考虑本层函数该执行的指令,至于剩下的函数,不关本层函数的事。
4.形参与实参:形参是在函数内部命名的参数,在函数内部发挥作用,可以作为实参的载体,在函数结束时回收。而实参是在函数头命名的参数,可以将值传给形参,不会在函数结束时回收。
5.有意义、规范的标识符:在编写代码时我们的格式一定要规范清晰,不仅仅是让我们自己能读懂更重要的是让别人也读懂,例如在hanoi问题中,三个柱子的命名为Source,transit,destination,而不是简单的ABC,这让别人一眼能够快速进入代码并理解代码。
6.时间复杂度: 我们假设Hanoi的执行时间是T(n),此递归函数中语句if(n==1)move(A, 1, C);的执行时间是O(1);else中递归调用的执行时间是T(n-1),赋值执行时间为O(1),所以执行时间是O(1) + T(n-1)。那么可以得到如下递归方程:
具体运算过程:
T(n) = 2T(n-1) + O(1);
T(n-1) = 2T(n-2) + O(1);
...
T(2) = 2T(1) + O(1).
除第一个式子之外全部*2,并加到上一个式子中,得到:
所以汉诺塔递归解法程序的时间复杂度是2^nO(1)。结果时间复杂度为O(2^n)【摘自博主:Kashine】
7.递归栈:建立在栈的基础之上,在第一个函数完成指令后弹栈,递归函数调用又将函数压栈,依次反复进行,直到该递归栈完成指令,这就是递归栈,本质是递归函数的调用和栈原理的结合。
8.空间复杂度:简单来说就是所占用的空间,在递归栈中,若有三个函数占用了3片空间,不论压栈弹栈,始终只有这三个函数,那么他的空间复杂度就可以看作3。