前言
递归,是编程中一个非常重要又迷人且容易让人摸不着头脑的算法。很多同学(包括本人)在刚接触递归的时候常常一脸懵逼,搞不清楚递归的定义,也不清楚到底怎么使用递归。而今天,就让我们一起走进迷人又危险的递归!
递归概念
当我们谈论一种算法时,首先我们要了解这个算法的概念,于是我们打开谷歌搜索
如上图,所以我们得知
递归是一种在计算机科学和数学中常用的概念,它指的是函数直接或间接地调用自身来解决问题的方法
又是一段看起来(听不懂)非常专业的叙述。大佬们,我是小白我听不懂啊,咋办,别急,让我们回到最初的原点,从头来认识一下递归的最初形态。
从阶乘聊起
阶乘,大家肯定不会陌生的一个概念,如果我们要算5!,则5!=5 4 *3 2 1。 根据基础小学知识来说,上面这个式子可以转变为5!=5 * (4 *3 2 1),而4 *3 *2 1=4!,所以等量代换,5!=5 * 4!。那么以此类推,4!=4 * 3!,*3!=3 * 3! 等等......我们很轻易就可以从上面的叙述中发现规律,即 n! = n * (n-1)!。什么意思呢,用中文来说就是。一个数的阶乘等于这个数乘与这个数的前一个数的阶乘。
- 我们用js代码实现上述思想则为
function jc(n){
if(n===1){
return 1
}
return n * jc(n-1)
}
jc(5)
// jc(5) => 5*jc(4)
// jc(4) => 4*jc(3)
// jc(3) => 3*jc(2)
// jc(2) => 2*jc(1)
// jc(1) => 1
// jc(n)===n*jc(n-1)
1. function jc(n) {
我们先创建一个名为 jc 的函数,接收一个参数 n,用于计算 n 的阶乘。
n是输入的数字,我们将计算n!(即n的阶乘)。
2. if (n === 1) {
这是一个条件语句,检查 n 是否等于 1。如果它等于1,我们就返回1
3. return n * jc(n - 1);
如果 n 不等于 1,那么执行这一行代码。
- 这里返回的是
n * jc(n - 1),即当前的数字n与调用jc(n - 1)的结果相乘。 这行代码会把n与jc(n-1)的返回值相乘,并且在函数内部又调用了jc,导致函数再次执行,直到n === 1。
例如,假设 n = 5,那就会执行:
5 * jc(4)
然后继续递归调用 jc(4),这会变成:
5 * (4 * jc(3))
再递归调用 jc(3),变成:
5 * (4 * (3 * jc(2)))
再递归调用 jc(2),变成:
5 * (4 * (3 * (2 * jc(1))))
当 n 递减到 1 时,这个循环停止,jc(1) 返回 1。
是的,这样不断在jc这个函数中调用自己的行为,就叫做递归!
斐波那契数列
现在你初步了解到了什么叫递归,那么让我们来看一个经典案例——斐波那契数列。斐波那契数列(Fibonacci Sequence)是一种由意大利数学家斐波那契在13世纪提出的数列,其特点是:从第三项开始,每一项都是前两项之和。数列的前几项为:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
用数学公式表示: F(n)=F(n−1)+F(n−2) 其中,初始条件是: F(0)=0,F(1)=1。
很好,聪明的你肯定又发现规律了,没错,F(n)=F(n−1)+F(n−2)!
当我们想要斐波那契的第n个数时,我们就可以应用这个规律,写一个代码。
- 首先,我们定义一个函数fb,传入一个n值,代表我们想知道的斐波那契数列的第n个数是多少
function fb(n){
}
- 继续思考,我们要知道第
n的数的值,那我们就得知道第n-1的数和第n-2的数的值,而要知道第n-1的数的值又得知道第(n-1)-1的数的值和第(n-1)-2的数的值。 - 所以函数的返回值为
(n-1)+(n-2)
function fb(n){
return fb(n-1) + fb(n-2)
}
- 就这么一直往前倒,要知道第5个数的值就得知道第4个和第3个数的值,要知道第4个数的值就得知道第3个和第2个数的值,要知道第3个数的值就得知道第2个和第1个数的值。而第2个和第1个数的值我们可以轻易得知=>第一个数为
0,第二个数为1 - 没错,第一个数和第二个数就是函数的出口,如果没有这个出口,这个函数就会陷入死循环。当
n=1时,返回第一个数的值0,当n=2时,返回第二个数的值也就是1
function fb(n){
if(n===2){
return 1
}else if(n===1){
return 0
}
return fb(n-1) + fb(n-2)
}
计算斐波那契数列第n个数的值所需要的代码就这样被我们写出来啦。
小结:递归两要素
从上面两个例子我们可以做一个小小的总结,要进行递归,简而言之就两句话
- 找规律
- 找出口
找规律指的是分析问题的结构,找到如何将一个复杂问题分解成更小的子问题(即重复的模式)。
-
关键点:
- 识别问题的递归关系,即大问题如何通过解决相似但规模更小的问题来推进。
- 把每一层递归的任务抽象成统一的逻辑,确保每次递归的输入都在向问题的边界条件靠近
找出口指的是找到递归停止的条件(边界条件),避免无限递归。
-
关键点:
- 出口条件必须明确、可达,且能够返回直接结果。
- 没有出口会导致程序陷入死循环,最终堆栈溢出。
写在最后
现在,聪明的你学会递归啦,递归在编程中是一个非常重要的思想,学会只是第一步,会用才是最终目标。一句话,当你看到重复的规律的时候,请你立刻发动你聪明的大脑,问问自己“欸?能不能用递归?”。当你用递归,又立刻想到“欸?出口在哪儿?”。当你能熟练得运用这个思想的时候,恭喜你,递归被你拿下啦!