本节主要说了递归的设计和算法实现,以及递归的基本例程斐波拉契数列、strlen的递归解法、汉诺塔和全排列递归算法。

一、递归的设计和实现

1.递归从实质上是一种数学的解决问题的思维,是一种分而治之的思想。

这个是常见的一种数学算法,其实它就是递归的本质。我们要求的是所有数的乘积,那么我们就先求出两个数的乘积,然后再根据这两个数的乘积去求第三个数的乘积,这样每一次我们实际上都是进行的两个数的相乘,也就是我们把一个很多个数的相乘转换为了两个数的相乘。

2.通过上面的例子可以发现,递归就是将大型复杂问题转化为与原问题相同,但是规模变小的问题进行处理。

4.同时我们可以发现a1 这个时候n==1,是一个特殊的条件。这就是递归的边界条件,最后的最后我们都会执行到递归的边界条件,然后再从边界条件返回。等到都返回结束后我们就真正实现了我们想要的结果。

5.如果递归没有边界条件,那么我们的递归将永远无法跳出,也就是这个问题递归是无法解决的。

6.在解决递归问题的时候首先要建立递归模型,这是解决递归类问题的第一步。但是说来容易,其实这是一个痛苦的过程,说白了,算法不是一般人能搞的。

二、斐波拉切数列的递归实现

1.斐波拉切数列实际上就是一个递归的典型表现,它的具体要求如下:

通过上图我们可以知道,斐波拉契数列的要求就是求相邻两个的数和然后赋给第三个数。这样我们可以先求前两个数的和,然后再求第二个与第三个数的和,一直求到最后,然后再返回。

2.假定我们要求的数列的元素个数为10

那么具体程序如下所示:

#include <stdio.h>int fibonacci(int n)
{if( n > 1 ){return fibonacci(n-1) + fibonacci(n-2);}else if( n == 1 ){return 1;}else if( n == 0 ){return 0;}
}int main()
{int i = 0;for(i=1; i<=10; i++){printf("fibonacci(%d) = %d\n", i, fibonacci(i));}return 0;
}

通过上面的程序可以看出:我们首先通过主函数调用fibonacci函数,然后通过for循环依次向里面传递值,第一次传递的值为1,返回的值为1,所以打印1.第二次传递的值为2,符合if(n>1)的条件,执行语句

fibonacci(n-1) + fibonacci(n-2);

这个时候产生第一轮递归,也就是先执行函数fibonacci(1)执行以后的返回结果是1,再执行fibonacci(0),执行以后的返回结果是0,所以这一轮的返回结果是是1.

继续调用fibonacci函数,传递的参数是3,然后依次向后执行,每一次的递归深度都在加深。

三、strlen函数使用递归方式实现

1.我们都知道strlen函数的使用方法,它是通过传递进来的字符串来判断字符串的大小,但遇见"\0"的时候返回字符的个数,"\0"不包括在内。
2.假定我们要求一个"12345"的字符串的长度,具体例程如下:

#include <stdio.h>int strlen(const char* s)
{if( s == NULL ){return -1;}else if( *s == '\0' ){return 0;}else{return strlen(s+1) + 1;}
}int main()
{printf("strlen(\"12345\") = %d\n", strlen("12345"));printf("strlen(NULL) = %d\n", strlen(NULL));printf("strlen(\"\") = %d\n", strlen(""));return 0;
}

程序分析:我们在主函数中调用strlen函数,在我们第一次进入strlen函数的时候,程序执行判定,都不满足前两个判定,程序继续向下执行,再次调用strlen函数,然后再进行判定,仍然不满足判定条件,一直执行到s指针指向'\0',这个时候各个调用函数开始返回,最外层的返回0,0+1,第二层返回1,1+1,第三层返回1,1+1 。直至所有函数全部返回。程序打印结果如下所示:

整个程序我们是把一个字符串的长度求解过程转变成了对每一个字符长度的求解,然后再进行相加,边界条件就是'\0'。

四、汉诺塔递归算法的实现

1.汉诺塔的要求我就不详细说了,汉诺塔的问题我想了足足一天,其实最后也是单步调试加上草稿纸才把它搞定,这里拿三个盘子作为分析。

2.汉诺塔的递归思想:第一,把a上的n-1个盘通过c移动到b。第二,把a上的最下面的盘移到c。第三,因为n-1个盘全在b上了,所以把b当做a重复以上步骤就好了。

3.汉诺塔的具体代码实现:

#include <stdio.h>void hanoi(int n, char a, char b, char c)
{if( n > 0 ){if( n == 1 ){printf("%c -> %c\n", a, c);}else{hanoi(n-1, a, c, b);printf("%c -> %c\n", a, c);hanoi(n-1, b, a, c);}}
}int main()
{hanoi(3, 'a', 'b', 'c');getchar(); return 0;
}

程序打印结果如下:

五、汉诺塔递归调用分析

因为为了调试方便观看值,所以我把a,b,c字符重新定义成了整型变量,具体程序如下:

#include <stdio.h>void hanoi(int n, int a, int b, int c)
{if( n > 0 )                                      {if( n == 1 ){printf("%d -> %d\n", a, c);}else{hanoi(n-1, a, c, b);printf("%d -> %d\n", a, c);hanoi(n-1, b, a, c);}}
}int main()
{hanoi(3, 5, 6, 7);return 0;
}

1.从主函数中调用hannoi函数,传递的参数是:n=3,a(1)=5,b(1)=6,c(1)=7
2.第1次进入hannoi函数,执行if(n==1)判定,不符合条件,调用hanoi(n-1,a,c,b)函数
3.向hannoi传递的参数是:n=2,a(2)=a(1),b(2)=c(1),c(2)=b(1)
4.第2次进入hannoi函数,执行if(n==1),不符合条件,调用hanoi(n-1,a,c,b)函数
5.向hannoi传递的参数:n=1,a(3)=a(2),b(3)=c(2),c(3)=b(2)
6.第3次进入hannoi函数,执行if(n==1)判定,符合条件,执行打印函数printf("%d->%d\n",a,c)这个时候打印函数里面的a,c是第3次调用hannoi函数传递进来的参数,也就是a3),c(3),追到原始值也就是a(1),c(1)。打印结果是:5->7
7.n=3的hannoi函数调用结束,子函数第1次执行结束。返回到n=2的hannoi函数调用位置,程序继续向下执行
8.调用打印函数:printf("%d->%d\n",a,c),这个时候打印函数的参数是n=2的时候hannoi函数的参数,也就是
a(2),c(2),追到原始值a(1),b(1)。打印结果是:5->6
9.执行完打印函数以后程序继续向下执行,调用hanoi(n-1,b,a,c)函数
10.这次调用hanoi(n-1,b,a,c)是从n=2的hannoi函数开始的,所以向hannoi函数传递的参数是:n=1,a(4)=b(2),b(4)=a(2),c(4)=c(2)
11.第4次进入hannoi函数,指定if(n==1)判定,符合条件,执行打印函数printf("%d->%d\n",a,c)这个时候打印函数里的a,c是a(4),c(4),追到原始值c(1),b(1),打印结果是:7->6.
12.调用hanoi(n-1,b,a,c)函数结束,程序返回到调用hanoi(n-1,b,a,c)函数的位置,接下来程序没有语句,子函数再次结束,程序返回到n=3调用hannoi函数的位置,执行印函printf("%d->%d\n",a,c),这个时候打印函数里的参数a,c是n=3的时候的参数,也就是a(1),c(1),追到原始值。打印结果是:5->7。
13.程序继续向下执行,调用hanoi(n-1,b,a,c)函数
14.这个时候第一轮递归已经结束,向hannoi传递的参数是:n=2,a(5)=b(1),b(5)=a(1),c(5)=c(1)
15.第5次进入hannoi函数,执行if(n==1)判定,不符合条件,调用hannoi(n-1,a,c,b)函数
16.向hannoi传递的参数是:n=1,a(6)=a(5),b(6)=c(5),c(6)=b(5)
17.第6次进入hannoi函数,执行if(n==1)判定,符合条件,执行打印函数printf("%d->%d\n",a,c)这个打印函数里面的参数a,c是第6次调用hannoi函数的参数,也就是a(6),c(6),即b(1),a(1),追到原始值为6,5,打印结果是6->5
18.这个时候第6次进入hannoi函数执行完毕,程序返回到第六次调用hannoi函数的位置,继续向下执行。
19.调用打印函数printf("%d->%d\n",a,c),这个时候打印函数的参数a,c是n=2的时候第5次调用hannoi函数传递的参数,也就是a(5),c(5),追到原始值是b(1),c(1),即6,7.打印结果是6->7
20.程序继续向下执行,调用hanoi(n-1,b,a,c)函数,这个时候程序是从n=2的hannoi函数位置继续向下执行的,参数是:n=1,a(7)=b(5),b(7)=a(5),c(7)=c(5)
21.第七次进入hannoi函数,进行if(n==1)判定,符合条件,执行打印函数printf("%d->%d\n",a,c),这个时候打印函数的参数是第七次调用hannoi函数的参数,也就是a(7),c(7)即b(5),c(5),追到原始值5,7打印结果是:5->7
22.程序最后再一次返回两次调用hanoi(n-1,b,a,c)函数的位置,但是每一次返回都没有其他动作,直至程序结束。

程序分析的结果十分复杂和繁琐,而且这仅仅是三层盘子。同时由于自己的画图水平不好,所以也没有画流程图,同时网上好多大牛说是可以用树的想法去想汉诺塔问题,但是我没学习到树,所以只能用上面那种最笨额方法了。

六、全排列的递归调用

1.问题的提出:假定有三个元素a,b,c,那么这三个元素的全排列有六种方式:abc,acb,bac,bca,cba,cab。那么两个元素的全排列的是ab,ba,一个元素的全排列就是元素本身,所以一个元素的全排列就是递归的边界条件。

2.我们这里以三个元素的全排列,程序例程如下:

#include <stdio.h>void permutation(char s[], int b, int e)
{if( (0 <= b) && (b <= e) ){if( b == e ){printf("%s\n", s);}else{int i = 0;for(i=b; i<=e; i++){char c = s[b];s[b] = s[i];s[i] = c;permutation(s, b+1, e);c = s[b];s[b] = s[i];s[i] = c;}}}
}int main()
{char s[] = "abcd";permutation(s, 0, 3);return 0;
}

程序的递归算法框图如下所示:

由于我们传进子函数的四个字符的字符数组,所以这里我们直接执行else部分的函数,首先执行for循环,for循环从i=b开始,这样我们第一轮for循环先做的交换代码如下:

char c = s[b];
s[b] = s[i];
s[i] = c;

其实这个时候我们没有完成任何交换,然后继续调用permutation(s, b+1, e)函数,再次进入for循环,这个时候for循环是从i=b+1开始的,这个时候执行交换部分的代码还会完成另一个元素的交换。实质上这个交换部分的代码完成的就是将每一个元素作为第一个位置的作用,然后再进行其他操作。
for循环里的第二部分主要代码如下:

c = s[b];
s[b] = s[i];
s[i] = c;

这一部分代码的主要作用是在每一个permutation(s, b+1, e)进行弹出操作的时候开始执行,这样我们就可以对后面的数进行全排列了。我们也就完成图示的全排列操作。具体的递归过程可以例程单步调试来进行理解。

C语言数据结构----递归的应用(斐波拉契数列、汉诺塔、strlen的递归算法)相关推荐

  1. 两个经典递归问题:菲波那契数列 + 汉诺塔

    一.递归问题的处理步骤 1)抽象出递归公式:对实际问题进行部分穷举,抽象出递归关系(关键),并列出"递归表达式" 2)确定递归出口:找出递归调用终止点 二.菲波那契数列 实际问题: ...

  2. C++递归或非递归实现求斐波拉契数列第n项

    斐波拉契竖列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144- 递归 #include <iostream> using namespace std;i ...

  3. 数据结构与算法—递归算法(从阶乘、斐波那契到汉诺塔的递归图解)

    目录 递归介绍 递归求阶乘 递归求斐波那契 递归解决汉诺塔 总结 递归介绍 递归:就是函数自己调用自己. 子问题须与原始问题为同样的事,或者更为简单: 递归通常可以简单的处理子问题,但是不一定是最好的 ...

  4. labview求n阶乘的和_递归算法(从阶乘、斐波那契到汉诺塔的递归图解)

    递归介绍 递归:就是函数自己调用自己. 子问题须与原始问题为同样的事,或者更为简单: 递归通常可以简单的处理子问题,但是不一定是最好的. 对于递归要分清以下概念: 自己调用自己 递归通常不在意具体操作 ...

  5. 递归算法小结(数的阶乘、斐波那契和汉诺塔问题)

    递归是一项重要的编程技术,它让函数可以从函数体内部调用自身.递归通常把一个大型复杂的问题层层简化为一个,与原问题相似的规模较小的问题来求解,使用递归策略只需少量的程序就可描述出解题过程所需要的多次重复 ...

  6. 数据结构Java02【栈、队列、单链表(增删节点)、循环链表、双向循环链表、递归(斐波那契、汉诺塔)】

    学习地址:[数据结构与算法基础-java版]                  

  7. c语言n次方怎么输入_C语言实现斐波拉契数列

    C语言实现斐波拉契数列教程 怎么使用 C 语言实现计算斐波拉契数列的第 N 项的值? C语言实现斐波拉契数列详解 背景知识 斐波那契数列是一组第一位和第二位为 1,从第三位开始,后一位是前两位和的一组 ...

  8. 数据结构之栈与递归的实现及应用(斐波拉契数列递归解法和strlen递归解法)

    栈与递归 程序中的"函数调用栈"是栈数据结构的一种应用. 函数调用栈一般是从高地址向低地址增长的,栈底为内存的高地址处,栈顶为内存的低地址处. 函数调用栈中存储的数据为活动记录.活 ...

  9. C语言递归算法求斐波那契,递归法求斐波那契数列(C语言版)

    斐波那契数列: 又称黄金分割数列,指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 在数学上,斐波纳契数列以如下被以递归的方法定义 ...

最新文章

  1. 小菜学设计模式——高内聚、低耦合
  2. jupyter notebook中的魔法函数
  3. 基于redhat linux虚拟服务器的web负载均衡集群(piranha+LVS)
  4. Apollo2.5摄像头安装
  5. nginx安装及负载均衡配置
  6. mysql使用方法_Mysql的常用用法
  7. 菜鸟的学习之路(13) —TreeSet类的排序
  8. 计算机发展简史ppt教程,计算机发展简史ppt课件.ppt
  9. jsdroid 教程_南方Cass专题,全系列教程+插件汇总打包
  10. 组合导航(五):惯性导航参数建模
  11. 通达信指标公式常用绘图函数(2)——DRAWLINE、DRAWKLINE、STICKLINE
  12. 计算机方面各种级别论文版面费,国内计算机类杂志投稿评价.doc
  13. Mysql onlineddl vs gh-ost
  14. 20155318 《Java程序设计》实验四 (Android程序设计)实验报告
  15. 2022年分布式I/O市场前景分析及研究报告
  16. k8s + nfs 静态存储模式
  17. 模拟器计算机内存不足,模拟器内存不足要怎么办_怎样修改模拟器的内存大小 - 驱动管家...
  18. 软件工程——软件开发阶段(概要设计、详细设计)
  19. 英国猫咪暖心故事:让我陪伴你一生
  20. 模式先行区块链商城将颠覆传统商城

热门文章

  1. js进阶 9-5 js如何确认form的提交和重置按钮
  2. 光伏发展路线图将发布 促产业优胜劣汰
  3. 判断一个字符串是否为另外一个字符串旋转之后的字符串。
  4. 操作主机 RID matser
  5. BZOJ.3265.志愿者招募加强版(费用流SPFA)
  6. 8-[多线程] 进程池线程池
  7. 解决安装Weblogic domain卡住问题(Primeton BPS)
  8. word-vba-microsoft(中英文)
  9. Ubuntu16.04 + caffe-ssd + [CPU_ONLY] + KITTI 训练总结
  10. C#中二进制和流之间的各种相互转换