风海网 > 生活 > 正文

​在c语言中如何使用递归函数(C语言学习篇27)

2023-08-23 21:41 来源:风海网 点击:

在c语言中如何使用递归函数(C语言学习篇27)

引言

前面我们讲了函数的组成部分以及传参详解,函数指针等知识,对函数相关知道有了一定了解,已经足够应对平时的开发和学习了。今天我们再来说说一个比较特殊的函数,就是我们的今天的主题——递归函数,为什么说是特殊呢,这是因为平时我们写的函数都是通过别人或者说是其他代码来调用的,而递归函数是自己来调用自己, 是不是觉得很特别呢?递归函数也是常常在笔试题中出现的哦~

计算阶乘

递归函数本质也是函数,只是函数的特殊性,应用在某些特殊的场景,以下我们介绍几种递归函数的应用场景,来感受下其美丽吧。以下是计算数字5阶乘,具体代码如下所示:

#includeint cal_factorial(int n) { //1.参数判断 if(n < 1) { printf("n必须大于等于1.\n"); return -1; } //2.退出递归条件判断 if(n == 1) {//2.1不满足递归条件,则退出 return 1; } else {//2.2满足递归条件,继续递归 return (n*cal_factorial(n-1)); } } int main(void) { int res = cal_factorial(5); printf("5! = %d.\n", res); return 0; }

分析:

1.首先我们定义了一个递归函数int cal_factorial(int n),传入参数n是指要计算阶乘的数,返回值为最终计算阶乘的结果。

2.在cal_factorial函数内部,首先判断传入的参数sizh是否符合规范,比如要计算的阶乘,那么必须是一个大于等于1的正整数。

3. 重点:接着if(n == 1)作为递归退出条件,因为我们知道当计算到乘以1时,说明已经完成了阶乘的计算(所谓的阶乘是指一个数n,一直乘以不断减1的数,直到乘以1为止:n*(n-1)*(n-2)....*1),而这里的return 1,是返回1本身,并与之前递归的结果(result=n*(n-1)*(n-2)....*2) 相乘(*1),得到完成的计算阶乘的式子。否则(else),返回 (n*cal_factorial(n-1)); 这里就用到递归, 这里我们以计算5阶乘为例子,那么第一调用该递归函数时,就会进到else语句部分中,即执行 5*cal_factorial(5-1),而这里的cal_factorial(5-1), 还是会进到else部分中,即变成了 5 * 4 * cal_factorial(4-1), 同理再次进入else部分,变成5*4*3*cal_factorial(3-1),接着还是同理变成5*4*3*2*cal_factorial(2-1),到了这里cal_factorial中传入参数等于1,再次调用递归函数时,就会进入if(n==1)语句部分,直接返回1,退出递归,此时结果就成了5*4*3*2*1。

4. 然后我们在主函数中定义一个int变量res来接收递归函数cal_factorial返回值,并通过printf打印

编译运行结果:

在c语言中如何使用递归函数(C语言学习篇27)(1)

使用递归函数的条件

首先要说明的是并不是所有的问题都能用递归解决,要使用递归函数的就必须具备以下2个条件:

要有递归的方式

递归的思想是指: 为了解决当前问题 F(n),就需要解决问题 F(n–1),而 F(n–1) 的解决依赖于 F(n–2) 的解决……就这样逐层分解,分解成很多相似的小事件,当最小的事件解决完之后,就能解决高层次的事件。这种“逐层分解,逐层合并”的方式就构成了递归的思想。

要有终止条件

每当进行一次递归,我们都需要判断条件是否满足,继续递归还是退出。如果没有递归终止条件或者这个条件不能被满足,则这个递归就没有收敛性,而没有收敛性是一个非常可怕的事情,因为函数递归占用的是栈内存,每次递归调用都会消耗一些栈内存,因此必须在栈内存耗尽之前就要完成递归收敛,否则就会出现栈溢出,严重时就会宕机。

#includevoid digui(int n) { int a[1000]; if(n > 1) { digui(n); } } int main(void) { digui(3); return 0; }

在上面我们定义了递归函数digui,并且没有做递归退出处理(digui(n)),导致一直递归下去,同时我们每次递归都定义1000个大小int类型的数组,用来撑爆栈内存。我们看看编译运行的结果:

在c语言中如何使用递归函数(C语言学习篇27)(2)

递归函数的执行顺序

递归函数的执行和返回就类似剥洋葱一般,首先一层一层的往里剥,剥到最深处后,再一层一层往外褪去,我们可以通过以下例子来感受下:

#includevoid digui(int n) { printf("递归前: n = %d.\n", n); if(n > 1) { digui(n - 1); } else { printf("结束递归. n = %d.\n", n ); } printf("递归后:n = %d.\n", n); } int main(void) { digui(3); return 0; }

说明:

1.首先我们定义一个递归函数void digui(int n), 判断n大于1时(if(n>1)),进入函数都打印递归的值

2. 当n=1时,进入else语句部分,打印结束递归,和n的值,这里n的值肯定为1哦

3. 接着我们打印递归后的n的值

4. 在主函数中我们向递归函数传入3,用来依次打印递归前后值的变化

我们先编译运行,看看结果:

在c语言中如何使用递归函数(C语言学习篇27)(3)

从结果我们可以看出,一进入递归函数,就会打印递归前的值(当前n的值), 然后一直执行if(n > 1)语句中的递归函数,直到n=1;递归函数才运行到printf("结束递归. n = %d.\n", n );(这里大家可以看到在此之前一直都没有执行printf("递归后:n = %d.\n", n))退出了if(n > 1)重复递归, 然后依次打印了3此递归后的值, 并且可以看到值是1, 2, 3。 这是理解递归执行顺序的重点: 首先打印的是1,而不是3, 这里是因为在之前一直递归到n=1(剥洋葱剥到最深处了),然后依次退出来, 顺序就是1,2,3。大家好好感受下其中的妙处。这是真正理解递归函数的重点,一定要掌握!

总结

递归的主要目的是为了简化程序设计,使结构简洁清晰,可读性强。 在数学方面应用的颇多,如典型的计算阶乘,求斐波那契数列等。但是递归的缺点也是很明显的,比如速度慢,运行效率低,对栈空间占用多,大量的函数调用,特别占用cpu,每次函数调用都需要保存上下文,因此大家在使用递归函数的时候需要慎重小心。

在c语言中如何使用递归函数(C语言学习篇27)(4)

,