C语言函数之递归调用

提示:本文主要是掌握函数的递归


前言

函数是学习C语言的最重要知识点之一,要学好这门编程语言,那么函数当然是至关重要的。


提示:以下是本篇文章正文内容,下面案例可供参考

什么是递归

在之前的博客“函数的基本认识”中,我们已经讲述过了函数的调用,那么它究竟是怎样进行调用的呢?
函数调用其实是通过栈来实现的。在调用函数的时候,系统会给被调用的函数分配空间,这块空间被安排在一个栈里面。每调用一次函数,系统就会自动在栈顶为它分配一次空间,退出一次则该空间也会被栈顶释放。而正在运行中的函数的存储区在栈顶。
由于栈顶的原则讲究先进后出,因此在有多个函数进行嵌套调用时,会满足先调用后返回。讲到这里
很多人都应该听说过这么一个故事:

从前有座山,山里有座庙,庙里有个老和尚在讲故事讲的是:从前有座山,山里有座庙,庙里有个老和尚在讲故事讲的是从前有座山,山里有个庙…

递归也是一种函数调用,是程序调用自身的编程技巧。
简单地来说、递归就是函数自己调用自己。它是一种特殊的函数调用,递归策略只需要少量的代码就能够描述出所需要的多次重复计算。
下面我们来写一个程序来更加直观地看看函数是如何调用自己的。

# include <stdio.h>

void Hello(int n); //函数声明

int main(void)
{
	int n = 0;
	printf("你需要输出多少个你好:");
	scanf("%d", &n);
	Hello(n); //函数调用
	return 0;
}

void Hello(int n) //函数的定义
{
	if (n > 0)
	{
		printf("你好 ");
		Hello(n - 1);
	}
	else
	{
		return;
	}
}

运行结果如下:
在这里插入图片描述
其调用函数的具体过程如下:
在这里插入图片描述
从上面这个运行过程来看,自己调用自己必须要知道自己什么时候停下来,否则就会一直不停调用自己,造成所说的“死递归”,直到存放数据的栈满了,没有地方放了为止,但是这个时候你的计算机也就死机了。


递归的两个必要条件

不是所有的问题都可以用递归来解决,刚才我们已经通过过程图知道,自己调用自己必须要知道自己什么时候停下来,否则就会形成“死递归”。因此使用递归的两个必要条件之一就是:要有终止条件(最小事件的解)。
下面我们来一起看看递归的思想是怎么样的。上面的过程图其实已经能够很好地体现了递归的思想,现在我们来总结一下:
为了能够解决F(n),就要先解决F(n - 1),为了能够解决F(n - 1), 就要先解决F(n - 2),为了能够解决F(n - 2),就要先解决… … …像这样逐层分解,当最小的问题化解之后,再循环渐进返回来化解更高层次的问题。这就是递归思想------逐层分解,逐层合并。
根据递归的思想,可以清楚其最主要的就是找到递归的出口和递归的方式。我们已经知道了递归出口其实就是终止递归的条件,也就是找到最小事件的解。
而递归的方式值的是用什么方式进行函数的递归调用,也就是递归公式。
综上,我们的出递归的两个必要条件是:
(1)知道递归的方式
(2)清楚递归的终止条件(最小问题的解)


递归与迭代的关系

迭代是循环的一种方式。理论上来说所有的循环都能够采用递归算法,然而能够使用递归来解决的问题,有时候循环却不能解决。


递归函数的优缺点

优点:
递归函数的优点在于能够提高代码的可读性、简化程序的设计、易于理解,而且刚才也提到了能够使用递归来解决的问题,循环却解决不了。采用递归能够将一个复杂的事件逐层分解成简单的事件。不仅如此,采用递归还能够很好出反映事件的本质,让我们从本质上去理解。当然,我们刚开始学习递归的时候可能会觉得力不从心,那是由于我们对于递归见识还很少,因此不用担心。
缺点:
虽然刚才描述递归是那么那么地好,但是递归的缺点也是非常明显的。为什么这样说?递归它的速度相对于其它的比较慢、而且运行的效率非常的低。在使用递归来解决经典斐波那契数列问题的时候这两个缺点真的是超级明显。
在这里插入图片描述
斐波那契数列:前两个数之和等于第三个数。
按照递归的思想,那么就需要逐层化解、逐层合并。如果我们要求第50个斐波那契数,按递归思路:第50个 = 49(个) + 48(个)、49 = 48 + 47…就是上面那个图的大致步骤,这个问题采用递归,运行过程中会出现多次重复计算相同的数据。
递归对于存储空间的占用也比循环占用的多,浪费太多的空间
在这里插入图片描述
每调用一次函数,系统就会自动为这个函数安排一个空间,而递归自身多次调用自己。递归是由栈机制实现的每深入一层都要占用一块栈数据区域,而内存空间是有限的,因而有时候对于一些事件,递归就显得格外力不从心。


什么时候使用递归

1.当该事件递归和非递归都能使用,且没有明显问题时;
2.当该事件采用递归十分简单、非递归比较难,且没有明显问题时;
3.如果使用递归写起来十分简单,但又明显问题时一定不能使用递归


总结

以上就是今天要讲的内容,本文介绍了什么是函数递归,使用递归的两个必要条件,递归的优缺点以及什么时候使用递归。其实在实际的编程中,递归使用的并不多,但是也好好地学,毕竟在考试和面试中常用于考验人的逻辑推理能力。递归从问题的最终目标逐层化解、逐层合并,它是一种逆向思维。我认为,递归算法和人解决问题的方法很相似,因而要相信自己是能够学好递归的~


  • 10
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
知识覆盖  基本程序设计技术,递归程序设计,程序结构,文件,结构体,类型定义 实验内容 1.分别调试课件中的给定n求Fibonacci(n)递归与非递归函数,并编写测试函数对两种或多种不同方法所需时间进行比较,且当某一轮计算所需时间超过给定最大时间量时(如超过10秒),停止计算。计算过程中要求输出类似如下格式的运行时间比较表(请用实际运行时间代替***): n 非递归法耗时(ms) 递归法耗时(ms) 1 *** *** 2 *** *** … x *** *** 提示:计时方法请参见参考书或课件。 2.统计给定的一组文本文件的英文单词、字符、数字字符、空白字符、英文字母和其他字符的个数请你扮演项目组程序员角色,认真阅读CodeForLab6.cpp和CodeForLab6.h中的开发要求,按照项目经理要求,完成规划好的各个函数。 1)请参看文档:实验6结构说明.pptx,理解CodeForLab6.h中的两个结构体类型。并请在实验报告中说明typedef的作用。 2)阅读函数AllocateSpaceForTextFilesInfo,查阅资料并在实验报告中说明函数malloc的具体功能。 3)阅读函数CountWordsOfEuropeanTxtFile,画出该函数流程图,查阅资料并在实验报告中说明其中的函数fopen或fclose的具体功能。 4)阅读CodeForLab6.cpp的各个函数的功能说明,补全代码实现如下各个函数的功能: PrepareBasicInfoOfFiles CountWordsInOneLine CountFilesInfo CountDifferentCharactersInFile, TestCountFilesInfo 5)调用TestCountFilesInfo验证各功能正确性 6)阅读函数ExportFilesInfoToWebPage,查阅资料并在实验报告中说明函数fprintf的功能。 7)(自选题)查阅资料自学一些网页格式,尝试修改ExportFilesInfoToWebPage,以使输出内容更为丰富多彩或个性,如点击文件名能打开实验数据文件,再如加入一些其他链接,如程序员的个人主页等。 注意: 1)请将CodeForLab6.cpp和CodeForLab6.h文件添加到你的工程中; 2)测试数据至少应包括DataForLab6.rar中的4个文本文件,建议再自编一些测试文件。 特别注意:请一定看清要求,做好准备,通过整理函数调用关系表理清程序框架,通过分析编写流程图理清每个函数的实现思路。 3. (自选题)编写程序,统计英文文件中的每个单词的出现次数(词频)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小酥诶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值