一篇文章带你弄清js中递归的奥秘!

305 阅读5分钟

前言

递归,是编程中一个非常重要又迷人且容易让人摸不着头脑的算法。很多同学(包括本人)在刚接触递归的时候常常一脸懵逼,搞不清楚递归的定义,也不清楚到底怎么使用递归。而今天,就让我们一起走进迷人又危险的递归!

p683781402.jpg

递归概念

当我们谈论一种算法时,首先我们要了解这个算法的概念,于是我们打开谷歌搜索

capture_20241127232716140.bmp 如上图,所以我们得知

递归是一种在计算机科学和数学中常用的概念,它指的是函数直接或间接地调用自身来解决问题的方法

又是一段看起来(听不懂)非常专业的叙述。大佬们,我是小白我听不懂啊,咋办,别急,让我们回到最初的原点,从头来认识一下递归的最初形态。

从阶乘聊起

阶乘,大家肯定不会陌生的一个概念,如果我们要算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) 的结果相乘。 这行代码会把 njc(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个数的值所需要的代码就这样被我们写出来啦。

小结:递归两要素

从上面两个例子我们可以做一个小小的总结,要进行递归,简而言之就两句话

  1. 找规律
  2. 找出口

找规律指的是分析问题的结构,找到如何将一个复杂问题分解成更小的子问题(即重复的模式)。

  • 关键点

    • 识别问题的递归关系,即大问题如何通过解决相似但规模更小的问题来推进。
    • 把每一层递归的任务抽象成统一的逻辑,确保每次递归的输入都在向问题的边界条件靠近

找出口指的是找到递归停止的条件(边界条件),避免无限递归。

  • 关键点

    • 出口条件必须明确、可达,且能够返回直接结果。
    • 没有出口会导致程序陷入死循环,最终堆栈溢出。

写在最后

现在,聪明的你学会递归啦,递归在编程中是一个非常重要的思想,学会只是第一步,会用才是最终目标。一句话,当你看到重复的规律的时候,请你立刻发动你聪明的大脑,问问自己“欸?能不能用递归?”。当你用递归,又立刻想到“欸?出口在哪儿?”。当你能熟练得运用这个思想的时候,恭喜你,递归被你拿下啦!

p463713872.webp