哈喽啊各位,真是,好久好久好久不见。这段时间实在是太过忙碌了昂,还望诸君见谅,接下来时间会松很多,咱们也会恢复正常更新速度啦
小希在这里祝诸君:期末不挂科,四六级都过!功不唐捐,玉汝于成!!!
今天昂,咱们来认识认识函数递归
一 . 什么是递归?
在我们的C语言中,“ 递归 ”是我们相当重要的一环,简而言之,递归就是——函数自己调用自己。这是一种高效解决问题的办法。
这里为诸君演示一个最简单的递归函数:
在main函数中调用main函数:
因为我们没有终止信息,程序就会一直调用,一直打印
这里代码最终会陷入死递归,导致栈溢出(Stack overflow)
在这里只是为了给诸君演示一下递归的基本形式,不是为了解决任何实际问题
二 . 递归的本质
在递归中:“ 递 ” 就是 “ 递推 ”,“ 归 ” 就是 “ 回归 ”
递归的本质就是,把一个大型复杂的问题层层转化为一个与原问题相似,但规模较小的子问题来求解,一直拆分到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是将大事化小的过程
三 . 递归的限制条件
四 . 递归举例
(1)计算 n 的阶乘 n!(不考虑溢出)
注意:一个正整数的阶乘(factorial)就是所有小于等于该数的正整数的乘积,且规定0的阶乘为1
因为我们知道,n 的阶乘公式为:n!= n * (n - 1)!,如:
又因为 0 的阶乘为 1 ,则当 n == 0 时,n 的阶乘为 1
由此我们可以得出计算时的两种情况:
那么接下来我们就可以来实现这个阶乘函数:
画图演推:
(2)按顺序打印一个整数的每一位
例如:输入1234,打印1 2 3 4
分析:如果n是一位数,n的每一位就是n自己,n是超过1位数的话,就得拆分每一位
但是这样我们就会发现一个问题:我们先得到的总是最低位的数,但题目要求我们需要顺位打印,这个时候我们就可以通过递归实现,先将它 / 10,直到 / 到最前方的一位,再打印,如:
画图推演:
五 . 递归和迭代
递归是一种很好很方便的编程技巧,但是我们需要将其运用在合适的环境下,并不是每一个环境中我们使用递归都会使代码更加简便,搞不好就会弄巧成拙
就如同我们举例的第一道题目,求一个数的阶乘。还记得我们在第一个题目的后面还特别注释了:不考虑栈溢出的问题,这是因为,我们运用递归的确是可以实现求阶乘的运算,但是我们在递归的调用时,会涉及到一些运用时的开销
所以在这个题型下,我们最优解应该是采用迭代的方式,也就是我们常说的循环,如:
在这个题型中,我们使用迭代的方式的效率是要高于使用递归的
由此可见,我们递归虽好,但不要滥用哦,只有当一个问题非常复杂,我们难以使用迭代的方式解决问题的时候,此时使用递归的简洁性便可弥补其使用带来的运行时的开销
六 . 斐波那契数
在这里我再来举一个例子来帮助诸君理解递归与迭代
斐波那契数列是什么呢?我这里就直接为诸君搬运百度百科了昂:
简而言之呢,就是这个斐波那契数列,前两项都为 1 ,此后的每一项都等于前两项之和
求第 n 个斐波那契数:
(1)运用递归方式
斐波那契数的运算原理如下,我们可以发现,这不就是我们的递归嘛
当我们求第 n 个斐波那契数的时候,它是不适合用递归的方式来求解的,这个时候就有小伙伴要真诚地发出疑问了:这是为什么呢?明明斐波那契数本身就是通过递归的方式来运算的,我们现在用递归的方式来实现它反而不合适了呢?Good question!!!
诸君不必心急,接下来咱们就先用递归的方式来实现它,我们看看会遇到哪些问题就明白了,毕竟实践才是检验真理的唯一标准嘛
此时我们可以看到,运用递归求求第 n 个斐波那契数没有任何问题,但当我们需要运算的数字非常大时,比如50,这个效率就非常慢昂:
反正我是等了大概3分多钟,有闲心的可以自己等一等哈哈,而且还得出了一个负数,这里应该就是出现了栈溢出的问题昂
通过递归计算效率极其低下的原因是:在我们递归的运行中,会有相当大量的重复计算,且递归的层次越深,冗余计算就会越多
如图,我们要通过递归计算第50个斐波那契数,这是一个倒序的运算过程,我们先要知道第49位的数和第48位数,我们要知道这两位数我们又得先知道第48,47,47,46位,以此类推......
由此可见,我们通过递归来实现它是还会有这非常大量的冗余计算的,所以效率极其低下
(2)运用迭代方式
当我们运用迭代方式来计算效率就高得多,无需多言,咱们直接上图昂:
运用了迭代的方式,没有了冗余的计算,咱们的运算效率就高得多昂,即使是50也是秒出答案
这就是递归和迭代的区别,咱们不能滥用,得根据题目所给出的环境选择合适的方法!
OKK,有关函数递归方面的知识今天咱们就谈到这里了,许久不见,甚是想念,还望各位继续与我一同前行,话不多说,咱们下期再见,与诸君共勉!!!