汉诺塔 是一个常见的简单递归问题
如果有三根柱子,分别为A,B,C;
A柱子上摆放着三个大小不同的圆盘,下面的比上面的要大。
每次只能移动一个圆盘,并不能使上面的圆盘比下面的大。
那么把A柱上的圆盘移动到C柱需要几步。
这就是汉诺塔的具体问题。
那么要解决这个问题 我们可以把这个过程分成三步。
1.首先把除了最底层的圆盘,其他都挪到B柱上。
2.然后把最底层的圆盘移动到C柱上。
3.最后把B柱的圆盘移动到C柱上。
那么根据上面的文字描述给出以下表达式(res(n)表示n个圆盘需要移动的次数):
res(n) = res(n - 1) + 1 + res(n - 1);
当然这样肯定没办法算出最终结果。我们需要给他一个初始值。
当n = 1(或0)时,res = 1(0)。
接下来就是完整的代码啦
#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. ----
汉诺塔问题具体分析:
1.自顶向下,逐渐求精,函数调用:
自顶向下就是把整个大的问题逐步拆分成几个小问题,从最开始的条件分析,以汉诺塔为例,整个求次数可以划分成三步:
先把n-1个圆盘移到一个柱子上,然后把底盘移到C柱上,最后把n-1个圆盘移动到C柱上。这就是解决问题的元步骤,通过
函数三步的调用就能算出最终的结果。
2.递归与分治(基础与归纳)
每次都是在n个盘子的移动中调用n-1个盘子移动的函数,然后制定一个下界,之后就是函数自己调用一步步从n走到0,最后
给出我们想要的结果,通过对每个调用的函数的执行就是用的分治的思想。
3.不要跨层分析
简单的来说就是我们发现他总的规律,就不用想他具体要怎么执行,系统会自动帮你把后面的步骤做完。
4.形参与实参(代码跟踪)
形参指的是函数中参数列表里的那几个元素,而实参是调用函数时,赋值给形参的原来的参数,也就是说调用函数的时候把
实参的值赋值给形参,这两个参数不公用同一地址。
5.有意义、规范的标识符
标识符为了让你过几个月还能一眼就看懂什么意思,需要用小驼峰命名法,给变量一个明确意义的名字。
6.时间复杂度
T(n)=2*T(n-1)+1;最后得出Tn=2^n;
7.递归栈(代码跟踪)
每次编译器读到函数的时候就会把这个函数push进栈,然后执行完之后才会pop出栈,所以如果在一个函数内部读到另一个
函数就会先让另一个函数入栈,出栈,做完才轮到外面那一层函数。下面是汉诺塔的递归栈的代码跟踪。
3 plates
----The funtion of 3 plant is start.----
----The funtion of 2 plant is start.----
----The funtion of 1 plant is start.----
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
A -> B
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
----The function of 1 plate is finish.----
A -> C
----The funtion of 1 plant is start.----
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
B -> C
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
----The function of 1 plate is finish.----
----The function of 2 plate is finish.----
A -> B
----The funtion of 2 plant is start.----
----The funtion of 1 plant is start.----
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
C -> A
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
----The function of 1 plate is finish.----
C -> B
----The funtion of 1 plant is start.----
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
A -> B
----The funtion of 0 plant is start.----
----The function of 0 plate is finish.----
----The function of 1 plate is finish.----
----The function of 2 plate is finish.----
----The function of 3 plate is finish.----
---- addToTest ends. ----
8.空间复杂度
根据上面递归栈的代码跟踪,我们会发现每次最多递归层数不会超过N层,也就是栈内使用到的空间最多只有N个空间。所以空间复杂度为O(n)。
线性表总结:
线性表从逻辑结构上分为顺序表和链式表:
1.顺序表:
在内存中属于同一片空间,连在一起。
顺序表的优势:查找速度快。
顺序表的劣势:添加删除速度慢,表容量固定。
2.链式表
在内存中属于不同空间,没有连在一起。
链式表的优势:添加速度快,内存动态分配。
链式表的劣势:查找速度慢。