函数的递归1

最简单的递归代码

递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。

接下来,我们用一条最简单的递归代码来展示

int main()
{
	printf("函数递归\n");
	main();
	return 0;
}

起初,我们会得到这样的结果

 其中,那个竖直光标在闪代表着一直在循环打印“函数递归”这四个字,在代码运行了一段时间之后,会出现

 可见,程序停止了。难道它不应该一直无限打印下去,为什么会出现终止的现象?

其实这是一个栈溢出的现象

如果我们将代码进行调试,会出现以下现象

 Stack overflow就代表栈溢出的现象,栈溢出的具体解释,我们会在后文涉及。

递归的思想

用一句话概括,递归就是把大事化小的过程,详细点说,把一个大型复杂问题层层转化为一个与原问题相似,但是规模较小的子问题来求解,知道子问题不能在被拆分,这就是递归。

当然,理解递归还得把递归两字进行拆分,递就是递推,归就是回归

递归的限制条件

我们在使用递推时,需要加上两个必要条件

1.递推存在限制条件,当满足这个限制条件的时候,递归便不在继续

2.每次递归调用之后越来越接近这个限制条件

具体的使用,我们得通过例子来进行讲解。

递推的例子

阶乘

在用递推代码实现阶乘之前我们要先简单介绍一下阶乘

阶乘是以n!的形式存在,如果是5!,代表的是5!= 5*4*3*2*1,即从数字1开始乘到数字n。

那我们要用函数递归的方式实现阶乘的计算,就得先实现递归的思想,把大事化小

由于5!= 5*4*3*2*1而4的阶乘为4!= 4*3*2*1,这样,我们是不是就可以写成5!= 5*4!的形式,同理4!= 4*3!,那么,我们可以将阶乘进行简化,得到公式

                                                          n!=n*(n-1)!

这样,我们就不用一个一个乘,简化了阶乘,符合大事化小的思想,那么接下来我们就要通过递推实现。

代码如下

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

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret=Fact(n);
	printf("%d", ret);

	return 0;
}

如果我们输入5,得到的结果是

 光看代码很难理解,我们可以通过画图的方式来解释

 首先我们只需看函数部分的代码,当我们输入5的时候,传输给Fact函数的形参就是5

此时,将会执行if语句,即

此时返回的值将是5*Fact(n-1)

由于这时候,调用了一次Fact函数,所以是Fact(n-1)的返回值和5相乘,但是函数的递推还没结束,n是5,则传输过去的参数则是n-1,即4 ,则又在函数内判断运行

 由于n依旧大于0,所以递推步骤还得继续进行,我直接用图像的形式把递推过程展现出来

 这是函数递推的过程,可以看到n逐渐减小,最后会减到0,那么,就得经历一个返回的过程了,接下来我们先上图,再一步步讲解

 当递推到最后一个函数时,此时n等于0,根据if条件语句的判断,满足小于等于0的条件,于是便执行return 1的语句,开始回归过程,n等于0时函数回归的值为1,返回到n=1时候的函数,再通过计算1*1,返回值为1返回到n=2的函数,就这样,以此类推,成功满足阶乘,乘到5,于是,5!的值120返回到主函数里,最后就打印出120来了。

这串代码能够打印5的阶乘,能打印6的阶乘,这些数都不算大,那能打印30,40或是50的阶乘吗,我们来试一下

 

 

可见,当我们输入40或50的时候,打印的是0,这并不代表40或50的阶乘就是0,而是返回的数太大,超出了内存空间,也就是我们之前所说的栈溢出现象

栈溢出

接下来,我们要对阶乘的栈溢出现象做个初步的解释

我们之前讲过内存中分为三个区域,栈区、堆区还有静态区。因此,我们需要知道,在C语言中每一次函数调用,都需要为本次函数调用在栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。

什么意思呢,用一张图来展现

 可以看出,栈区有一定的内存空间,每调用一次函数,局部变量和函数都会创建自己的函数栈帧一次来占据栈区的空间,由于栈区的空间有限,所以调用函数的次数越多或者说是递归层次太深,栈区剩下的空间越少,如果调用次数过多,那么栈区的空间就会被占满,甚至出现栈溢出的现象,所以,输入40或50的时候打印的是0,这时候已经出现了栈溢出。

难道,栈区的空间会一直被占着吗?

其实并不是

函数不反悔,函数对应的栈帧空间就一直占用,所以如果函数调用中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,知道函数递归不再继续,开始回归,才逐层释放空间

 上图就是返回后的栈区,内存全部释放

其实除了递归的方式,还有一种方式叫做迭代的方式(通常就是循环的方式)

我们可以用代码的形式表示

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

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret=Fact(n);
	printf("%d", ret);

	return 0;
}

用一个循环,便能实现

结语

这篇博客,我们通过一个阶乘的例子讲解了递归,下篇博客我们将讲解斐波那契数列的例子

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值