函数的递归


前言

哈喽小伙伴们!又见面啦!前面呢,我们分享了C语言中的函数,今天呢 。小编在想跟大家分享分享函数递归。那什么是函数递归呢?那就让我们怀揣这一刻求识的心一起进步吧!
在这里插入图片描述


一、递归是什么?

递归呢,其实就是一种解决问题的方法,在C语言中递归就是函数自己调用自己,
就像这样:

#include<stdio.h>
int main()
{
	printf("haha\n");
	main();
	return 0;
	
}

像这样就会一直打印”haha“直到函数将导致运行时堆栈溢出才停下来。

1.递归思想
其实递归的思维方式就是把大事化小的思想,将一个很复杂的问题分成许多个小的子问题来求解,直至不能在拆分。递归结束!

递归中的递就是递推的意思,归就是回归的意思,接下来慢慢来体会。

  1. 递归的限制条件
    递归在书写的时候,有2个必要条件:

• 递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。

• 每次递归调⽤之后越来越接近这个限制条件。

在下⾯的例⼦中,我们逐步体会这2个限制条件

二、递归举例

举例1求n的阶乘
⼀个正整数的阶乘(factorial)是所有⼩于及等于该数的正整数的积,并且0的阶乘为1。⾃然数n的阶乘写作n!。

题⽬:计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
分析:我们知道n的阶乘的公式: n! = n ∗ (n − 1)!
注意:0的阶乘时1 ( 0!=1)

在这里插入图片描述
像这样把一个较大的问题转化成几个与原问题相似且规模较小的问题进行求解,

通常呢,我们一看见这个问题就会想到循环。除此之外呢。迭代的⽅式也是个不错的选择,后面小编会讲到的。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int a, sum = 0;
	int ret = 1;
	scanf("%d", &a);
	for (int i = 1; i <= a; i++)
	{
		ret = ret *= i;
		sum += ret;
	}
	printf("%d", ret);
	return 0;
}

我们可以发现这样也可以解决问题,那如果我们用递归完成这个问题呢?假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶乘函数如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int Fact(int x)
{
	if (x == 0)
	{
		return 1;
	}
	else
	{
		return x * Fact(x - 1);
	}

}
int main()
{
	int a;
	scanf("%d", &a);
	int ret = Fact(a);
	printf("%d", ret);
	return 0;
	//运⾏结果(这⾥不考虑n太⼤的情况,n太⼤存在溢出):
}

输出的结果是:
阶乘
如果光看代码的话小伙伴们肯定有点难理解,那现在小编就给大家推演一遍以上代码的运行过程吧!大家请看这张图:
在这里插入图片描述
这边回归的结果是由把值代入左边计算在算出来:从Fact(0)开始返回去计算:然后再以此类推。

好啦!明白了上面的知识之后,我们再来看看下面这个问题:

举例2顺序打印⼀个整数的每⼀位

输⼊⼀个整数a,打印这个按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234 输出:1 2 3 4
输⼊:520 输出:5 2 0

分析:
这个题⽬,放在我们⾯前,⾸先想到的是,怎么得到这个数的每⼀位呢?如果a是⼀位数,a的每⼀位就是a⾃⼰
a是超过1位数的话,就得拆分每⼀位
1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4
然后继续对123%10,就得到了3,再除10去掉3,以此类推
不断的 %10 和 /10 操作,直到1234的每⼀位都得到;

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int print(int x)
{
	if (x > 9)
	{
		print(x / 10);
	}
	printf("%d ", x % 10);
	return 0;

}
int main()
{
	int a;
	scanf("%d", &a);
	print(a);
	return 0;
}

在这个解题的过程中,我们就是使⽤了⼤事化⼩的思路:
把Print(1234) 打印1234每⼀位,拆解为⾸先Print(123)打印123的每⼀位,再打印得到的4
把Print(123) 打印123每⼀位,拆解为⾸先Print(12)打印12的每⼀位,再打印得到的3
直到Print打印的是⼀位数,直接打印就⾏。

在这里插入图片描述

二、递归与迭代

递归是⼀种很好的编程技巧,但是很多技巧⼀样,也是可能被误⽤的,就像举例1⼀样,看到推导的公式,很容易就被写成递归的形式:
在这里插入图片描述

int Fact(int n)
{
 if(n==0)
 return 1;
 else
 return n*Fact(n-1);
}

Fact函数是可以产⽣正确的结果,但是在递归函数调⽤的过程中涉及⼀些运⾏时的开销。
在C语⾔中每⼀次函数调⽤,都要需要为本次函数调⽤在栈区申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈或者函数栈帧。

函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。

所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题。

所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)。
⽐如:计算n的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的。

int Fact(int n)
{
 int i = 0;
 int ret = 1;
 for(i=1; i<=n; i++)
 {
 ret *= i;
 }
 return ret;
}

上述代码是能够完成任务,并且效率是⽐递归的⽅式更好的
事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰但是这些问题的迭代实现往往⽐递归实现效率更⾼。

当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。


总结

这里呢,小编就已经分享完了关于递归函数的相关知识,可能不是很详细,但是小编会努力发现不足,努力完善自身实力,努力提高文章的质量,给大家带来更好的文章,好啦!今天就分享到这里吧!咋们下期再见。今天的你,又学到新的知识了吗?
在这里插入图片描述

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值