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

本节主要说了递归的设计和算法实现,以及递归的基本例程斐波拉契数列、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)进行弹出操作的时候开始执行,这样我们就可以对后面的数进行全排列了。我们也就完成图示的全排列操作。具体的递归过程可以例程单步调试来进行理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【物理动图】物理老师一针见血:50张动图看懂高中物理

全世界只有3.14 % 的人关注了青少年数学之旅相互作用作用力与反作用力的特点是什么&#xff1f;摩擦力的大小跟什么有关系&#xff1f;图中两个弹簧的弹力大小是否一样&#xff1f;若一样&#xff0c;为什么&#xff1f;自行车前后轮所受力的方向是什么&#xff1f;物块所受支持…

打造史上最小尺寸.Net Core单文件应用程序

.Net Core支持将应用程序发布成单文件进行部署和分发。以下示例将Windows应用作为独立的单文件应用程序发布&#xff1a;dotnet publish -r win-x64 -c Release /p:PublishSingleFiletrue /p:PublishTrimmedtrue /p:IncludeNativeLibrariesForSelfExtracttrue查看publish目录&a…

华硕路由器 linux上不了网,华硕ASUS路由器连不上网怎么办?

在本文中将给大家详细的介绍&#xff0c;华硕(ASUS)路由器连不上网/无法上网的解决办法&#xff0c;请按照下面的步骤进行操作。1. 首先&#xff0c;检查你的宽带是否可以正常使用&#xff0c;可以通过下面的方法进行测试。(1)观察光猫上的指示灯&#xff0c;如果光信号或者LOS…

NHibernate学习笔记(二):one-to-one关系映射

上一篇&#xff1a;NHibernate学习笔记&#xff08;一&#xff09;&#xff1a;初识NHibernate本文的内容&#xff1a;&#xff11;&#xff0e;介绍NH如何处理对象间one-to-ont的映射关系&#xff1b;经验教训&#xff1a;&#xff11;&#xff0e;操作一对一关联关系中的一个…

【汇总】多种方法教你绕过 TPM 2.0 安装 Windows 11 操作系统

此前我们曾介绍三种方法绕过 TPM 2.0 来安装 Windows 11 操作系统。方法一&#xff1a;删除 appraiserres.dll 文件方法二&#xff1a;替换 appraiserres.dll 文件方法三&#xff1a;替换 install.wim 文件今儿我们再谈谈“大法好”的注册表&#xff0c;希望能帮助大家成功安装…

【物理笑话】学过物理的人才能看懂的笑话,你能看明白几个?

全世界只有3.14 % 的人关注了青少年数学之旅1丈夫买了几斤廉价藕&#xff0c;满以为可对妻子炫耀了。不料妻子破口大骂&#xff1a;笨蛋&#xff01;为何不买别的菜&#xff0c;这藕一斤少说也有半斤窟窿啊&#xff01;还说便宜&#xff1f;2第一次坐飞机的两位老妇人在飞机起飞…

为什么国外程序员加班少?他们这样评价国内996和技术公众号

有人统计过&#xff0c;我们平均每天花在看内容上的时间是5-6小时与其每天被各种看过就忘的内容占据时间不如看点真正对你有价值的信息下面小编为你推荐几个高价值的公众号它们提供的信息能真正提高你生活的质量长按二维码&#xff0c;选择【识别图中二维码】关注Python爱好者社…

c语言编程每日一练教程,每日一练 | C语言之指针

原标题&#xff1a;每日一练 | C语言之指针练习导言学习 C 语言的指针既简单又有趣。通过指针&#xff0c;可以简化一些 C 编程任务的执行&#xff0c;还有一些任务&#xff0c;如动态内存分配&#xff0c;没有指针是无法执行的。所以&#xff0c;想要成为一名优秀的 C 程序员&…

dynamic flash xml news----滚动新闻

今天有人问起这个问题&#xff0c;抽出晚上的一点时间&#xff0c;做了一个&#xff0c;时间紧难免有不足之处&#xff0c;如果发现bug&#xff0c;请以在贴出。演示&#xff1a;代码&#xff1a;代码://copyright by webstudio.com.cn 2005-4-7 system.useCodepagetrue; Stage…

C# 代码生成二维码方法及代码示例(QRCoder)

背景二维码是越来越流行了&#xff0c;很多地方都有可能是使用到。如果是静态的二维码还是比较好处理的&#xff0c;通过在线工具就可以直接生成一张二维码图片&#xff0c;比如&#xff1a;草料二维码。但有的时候是需要动态生成的&#xff08;根据动态数据生成&#xff09;&a…

你对手机打字一无所知!| 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;视频源网络&#xff0c;侵权删&#xff09;你真的会打字吗↓ ↓ ↓

并行中的分区Partitioner

本篇介绍在C#中&#xff0c;把一个大集合&#xff0c;或大数组分成若干个区来执行。Demo中是把一组字符串放在list中&#xff0c;然后并行生成MD5串&#xff0c;返回回来。using System; using System.Collections.Generic; using System.Reflection; using System.Threading.T…

搞笑诺贝尔颁出,中国科学家入选!阿蟑有磁性、睾丸热不对称,10大奇葩研究来了...

全世界只有3.14 % 的人关注了青少年数学之旅Laugh and think&#xff0c;科学的另一面&#xff0c;就是有趣&#xff01;——在今天的哈佛大学桑德斯剧场&#xff0c;一群科学家身体力行地证明了这件事&#xff0c;在诺贝尔奖颁出前夕&#xff0c;率先发布了今年的“搞笑诺贝尔…

C++中的对象数组

类是对象的抽象&#xff0c;我们可以使用一个类来定义很多的对象&#xff0c;然后每个对象都有自己的属性。 当我们使用类来定义很多相同结构的对象的时候&#xff0c;我们可以采取对象数组的方法。 例如&#xff0c;一个班有50个学生&#xff0c;我们定义了一个学生类&#xf…

周杰伦新歌《说好不哭》彩蛋大汇总! | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅一首单曲&#xff0c;就霸占了一半的微博热搜榜&#xff0c;除了周杰伦&#xff0c;估计也没别人能做得到了。回想起前段时间&#xff0c;周杰伦的中老年粉丝与ikun们围绕着“顶级流量”展开的那场battle...说实话&#xff0c;那场 b…

AI日报:2024年人工智能对各行业初创企业的影响

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 2024年人工智能对初创企业的影响具体行业医疗金融服务运输与物流等 新趋势 2024年人工智能对初创企业的影响 2023年见证了人工智能在各个行业的快速采用和创新。随着我们步入2024年&#xff0c;人工智能初创公司正…

Ant Design Blazor 组件库的路由复用多标签页介绍

前言Blazor 是 .NET 最新的前端框架&#xff0c;可以基于 WebAssembly 或 SignalR &#xff08;WebSocket&#xff09;构建前端应用程序&#xff0c;基于 WebAssembly 托管模型的 Blazor 甚至可以离线运行。再加上可以共用 .NET 类库&#xff0c;能使代码量比以往的基于 JS 的前…

AI 竟然通过了初中生考试!?这意味着什么?

全世界只有3.14 % 的人关注了青少年数学之旅2016 年 AlphaGo 战胜世界棋王李世石&#xff0c;被认为是人工智能一个重要的里程碑。此后 AlphaGo 又击败了世界排名第一的围棋选手柯洁&#xff0c;AI 也在德州扑克、Dota 2 等游戏上战胜了专业的人类玩家&#xff0c;越来越聪明的…

基于Yarp实现内网http穿透

Yarp介绍YARP是微软开源的用来代理服务器的反向代理组件&#xff0c;可实现的功能类似于nginx。基于YARP&#xff0c;开发者可以非常快速的开发一个性能不错的小nginx&#xff0c;用于代理http(s)请求到上游的http(s)服务。http穿透原理同网现象在http反向代理里&#xff0c;代…

男科医生到底有多不正经… | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源真是个鬼才&#xff09;赶紧转给了身边有这种经历的朋友↓ ↓ ↓