浅谈递归概念

前言

        递归,学编程的人肯定会接触的一个词,笔者第一次接触到它是在刘慈欣的《镜子》一文里:如果超弦计算机尝试查看未来时,它会不断调用自己,直到无限的内存被用完,形成单向递归。在C语言中,它的内容和迭代有些类似但又不完全一样,今天就借本篇博客来浅谈一下递归的概念。

概念

        递归,就是将大的问题转化为若干个类似但是规模较小的子问题,一直到所有的子问题都不能再分,递归就结束了,换句话说,递归就是大事化小,小事化了的过程

        递归的思想可以从字面上理解,递和归,分别指代递推回归,接下来我们慢慢体会。

条件

        书写递归时需要满足如下两个条件:

        其一,递归需要存在一个限制条件,当达到限制条件时,递归便停止;

        其二,递归的每次调用都会越来越接近限制条件。

案例

        下面我们以两个例子来看看递归到底怎么回事。

案例一、阶乘

1.1   原理

        n的阶乘就是n!=n*(n-1)*(n-2)......*2*1,这个大家都学过,就不过多赘述了。

        tips:这里的“!=”不是C语言中不等于的意思

1.2   思路

        我们发现n!可以写成n*(n-1)!,而(n-1)!又可写成(n-1)*(n-2)!,那么以此类推,我们就可以推到n为1或者0时(0的阶乘也是1,因为阶乘的最初含义是全排列)。

1.3   代码实现

#include<stdio.h>

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

int main()
{
	int n = 0;
	scanf("%d", &n);
	int res = Fact(n);
	printf("结果为:%d", res);
	return 0;
}

        以5为例,它的结果如下

         到这里还是很正常且愉快的,但是当我们换个大一点的数字,比如90,它就会出现错误(见下图),这种错误的诱因叫做栈溢出,我们稍后再说。

1.4   运行过程

        这里我用平板画一下运行过程,相信还是比较明确的。

案例二、斐波那契数列

2.1   原理

        斐波那契数列就是一个数列,它的第一项和第二项为1,之后每一项的值都为前两项之和,这个大家也很熟悉,就不再赘述了。

2.2   思路

        假如我们想要求第n个项的值,我们可以把它分成第n-1个和第n-2个项的和,之后再继续分下去。看到这里大家可能也发现这种方法似乎有些低效,不过我们先往下看。

2.3   代码实现

#include<stdio.h>

int Fib(int n)
{
	if (n <=2)
		return 1;
	else
		return Fib(n-1)+Fib(n-2);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int res = Fib(n);
	printf("结果为:%d", res);
	return 0;
}

        以5为例,它的运行结果如下

        口算一下,1,1,2,3,5,显然正确的,但是再换个大点的数字,比如50,我们再试试

        在运行了大概半分钟以后,它成功地失败了,给了一个贵物答案,其实思考一下我们也可以发现,用递归来计算第50项的时候会产生大量的冗余计算,导致程序的运行十分低效,下面我们对代码稍微修改一下,以40为例,看看得到这个结果需要计算多少次第3项

#include<stdio.h>

int count = 0;

int Fib(int n)
{
	if (n == 3)
		count++;
	if (n <=2)
		return 1;
	else
		return Fib(n-1)+Fib(n-2);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int res = Fib(n);
	printf("结果为:%d\n", res);
	printf("\ncount = %d", count);
	return 0;
}

        它的运行结果如下

        数一数,发现第3项被重复计算了快4000万次,这显然是不合理的。

2.4   代码优化

        考虑到从结果推到开头,再从开头推回结果的过程实在繁琐,我们不妨直接使用迭代的方法来计算,也就是从第3项开始一个一个往后数,不过由于迭代并不是本篇文章的重点,这里就不再列出代码,感兴趣的朋友可以自己尝试一下。

问题解析

        在我们的电脑的内存中,有一个部分叫做栈(stack),在C语言的函数调用中,每一次的调用都需要在栈区申请一块内存空间来保存函数调用期间时各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧

        如果函数没有返回,那么函数栈帧就会被一直占用,所以在递归调用时,如果递归没有停止,栈帧空间就会不断增加,直到递推不再进行,开始回归,栈帧空间便会逐层释放。显然,内存中栈的空间是有限的,当递归层次过深,栈帧空间就会被过度占用,从而可能导致栈溢出(stack overflow)以及之后的一系列问题。

优点与缺点

优点

        递归的优点就是简单,它的书写与理解比迭代简单的多,而且在次数较小时递归与迭代并没有很大的差别

缺点

        递归的缺点就是复杂,这并不与简单矛盾,而是指它的运行十分复杂甚至说臃肿,在次数较大时经常会带来一些意想不到的问题,而且一般来说递归能做到的用迭代也可以实现,所以到底是选择递归还是迭代,还是根据实际情况判断较好。

后记

        第一次写这么长,我好厉害哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值