汉诺塔问题:
一、自顶向下,逐渐求精。
自顶向下将汉诺塔问题分解为多个相对简单的小问题并逐一解决。
二、函数调用,递归与分治。
程序调用自身的编程技巧称为递归。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。汉诺塔问题可以通过函数递归和分治进行简化,进而解决问题。
三、不要跨层分析。
诸如此类递归问题,在理解与分析过程中应逐层递进,不能跨层分析导致逻辑混乱。
四、行参与实参。
形参变量是功能函数里的变量,只有在被调用的时候才分配内存单元,调用结束后立即释放。所以形参只在函数内部有效。实参可以是常量,变量,表达式,函数等等,但无论是何类型,在进行函数调用是,他们必须有确定的值,以便把这些值拷贝给形参。在函数运行时,形参和实参是不同的变量,他们在内存中处于不同的位置。形参将实参的内容拷贝一份,在该函数运行结束的时候释放,实参内容不变。
五、有意义,规范的标识符。
为了让代码更具有可读性和辨认性,有意义且规范的标识符能达到这种效果,同时能让逻辑更加清晰。
六、时间复杂度。
汉诺塔问题的时间复杂度为O(2ⁿ),由于T(n) = 2T(n - 1) + 1,T(1) = 1,可以推出:T(n) = 2ⁿ − 1,时间复杂度就是O(2ⁿ)。
七、空间复杂度。
就汉诺塔问题而言,其空间复杂度与盘子数量有关,即与n有关,所以空间复杂度为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("%c -> %c \r\n", paraSource, paraDestination);
hanoi(paraN-1, paraTransit, paraDestination, paraSource);
} // Of if
} // Of hanoi
/**
* 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");
} // Of addToTest
/**
* The entrance.
*/
void main(){
hanoiTest();
} // Of main
运行结果如下:
---- addToTest begins. ----
2 plates
A -> C
A -> B
C -> B
3 plates
A -> B
A -> C
B -> C
A -> B
C -> A
C -> B
A -> B
---- addToTest ends. ----
--------------------------------
Process exited after 0.0353 seconds with return value 0
请按任意键继续. . .