深入浅出讲函数递归

函数递归之汉诺塔问题与青蛙跳台阶问题

1.函数递归的定义
2. 递归的两个必要条件
3. 错误应用与栈溢出
4. 汉诺塔问题
5. 青蛙跳台阶问题

函数递归的定义

函数递归,实际上就是函数自己调用自己的一个过程。就像剥洋葱一样,函数递归实际上就是把一个大型的复杂问题层层转化为一个与原问题相似,但规模较小的问题求解,直到原问题不能在被拆分的一个过程。用求N的阶乘这一个大家熟知的例子来说明:
在这里插入图片描述

递归的两个必要条件

何为必要条件?即递归若包含这两个条件也不一定是对的,但若不包含则一定是错的。

  1. 递归存在限制条件,当满足这一限制条件时,递归则不再继续。
  2. 每一轮递归后,都会越来越接近限制条件

看到这两个条件,我们是否想起了for循环的定义呢?这说明了,递归和循环(也叫做迭代)之间,存在一定的相似性。

回到主题,我们如果无法满足这两个条件的其中一个,或者错误应用递归,则很容易引起栈溢出(stack overflow)
这就引出了我们接下来的主题:

错误应用与栈溢出

首先我们要知道,每一次函数调用,都会向内存栈区申请空间。空间主要用于存放函数中的局部变量函数调用过程中的上下文信息。该空间叫做函数的运行时堆栈,也叫做函数栈帧空间。

如果函数调用过程中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归开始回归,才逐层释放空间。

如果递归层次太深(注意,这不是次数!),就会浪费太多栈帧空间,从而引起栈溢出。

这里有一个错误的例子:图在这里插入图片描述
在这里插入图片描述

汉诺塔问题

前面铺垫了这么多,我们终于来到了重头戏。在运用递归来解决汉诺塔问题之前,我们首先要了解,汉诺塔问题究竟是什么。
在这里插入图片描述

我们可以从图中看到三个铁架和几块大小不一的铁饼。
汉诺塔游戏的目的是将这几块铁饼按照原来的大小顺序,原封不动地将这几块铁饼由原来的铁架转移到另一个铁架上去,过程必须满足两个条件:

  1. 每一次只能移动一块铁饼。
  2. 大的铁饼不能放在小的上面。

让我们试着寻找,随着铁饼的增加,移动的次数是否有什么规律。当铁饼数量为1时,次数为1。当铁饼数量为2时,移动次数为3。当铁饼数量接着增加,移动的次数依次变为7,15,31…我们会发现,假设铁饼数量为n,则需要移动的次数变为2的n次方减1。这又是怎么得来的呢?

根据函数递归的思想,我们可以将移动n个铁饼的问题,转化为先移动n-1个铁饼,再移动第n个铁饼这两个小问题。

也就是说,如果我们的目的是将架1的n个铁饼通过架2移动到架3,假设移动n-1个铁饼需要移动k次,那么移动n个铁饼,实际上就是先将n-1个铁饼移动到架2,再将第n个铁饼移动到架3,最后再将n-1个铁饼移动到架3.也就是说,移动n个铁饼,实际上需要移动(2k+1)次

了解了汉诺塔问题的原理,让我们来代码实现一下:
在这里插入图片描述
通过这个代码中打印出来的结果,我们就可以得到移动n个铁饼需要完成哪些步骤了。有兴趣的小伙伴也可以试着改一改这个代码,让它可以打印出移动的步数!

青蛙跳台阶问题

说到青蛙跳台阶问题,就不得不提到斐波那契数列。相信大家对于斐波那契数列都十分熟悉,那么这两者又有何相同之处呢?
首先我们要知道,斐波那契数列规定除了第一和第二个数字都为一之外,之后的数字都为前两个数字之和。而青蛙跳台阶规定,假设有一只青蛙,它要跳上n个台阶,青蛙一次性可以跳过一个或者两个台阶,求共有多少种跳法。
这两者乍一看风牛马不相及,实际上非常相似。青蛙跳台阶的最后一跳,要么是1个,要么是2个,只有两种可能性,所以青蛙跳n个台阶的跳法,实际上就是它跳n-1个台阶和跳n-2个台阶的跳法之和

假设有 n 个台阶时,求有 ret 种跳法。让我们上代码:

在这里插入图片描述
在这里插入图片描述

当我们运行代码时,我们会发现,当有40个台阶时,结果很快就出来了,但当有50个台阶时,程序走了很久都没有出结果。这时有人就疑惑了,为什么这里不会出现栈溢出的情况呢?这就与我们先前相呼应了。

对于递归函数而言,是否会栈溢出主要和递归调用的层次有关,而与递归调用的次数无关。即使我们实际调用该函数的次数达到了 ,但实际上,在这个代码,递归函数的层次也只不过是50 而已。

但是,就如同斐波那契数列一样,随着台阶数额的增加,调用递归函数的次数也急速增长,从而产生大量的重复计算,大大降低了计算的效率。因此,利用循环的思想来解决这类问题是最好的。上代码:

在这里插入图片描述

从上面的代码我们不难看出,虽然两者在定义上很相似,但它们实现上的不同使得其各有优缺点。比如递归更易实现,某些问题中却失去了效率。循环的效率很高,却不容易想到。两者各有所长,全看个人喜好。

在循环语句中,主要是for循环,while循环,与do while循环。这里不再过多赘述。

下一篇主要会讲到整型提升与算术转换。不仅会讲到两者的概念,还会具体深挖整型提升的计算过程,附加算术转换的顺序列表。可以小小地期待一下!

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值