Go to comments

JavaScript 递归

递归最典型的就是两个,

1. 一个是阶乘,

2. 一个是斐波那契数列


四、写一个函数,实现n的阶乘

第一种方法用循环

第二钟方法用递归


1、循环

function mul(n){

    var num = 1;    

    for(var i = 1; i <= n; i ++) {
        num *= i;
    }

    return num;
}

console.log(mul(5)); // 120

从1到自身的数乘一遍(累乘)

num *= i

1   x  1 = 1

1   x  2 = 2

2   x  3 = 6

6   x  4 = 24

24 x  5 = 120


2、递归

阶乘的规律?

n阶乘的规律绝对不是从n乘到1,这是阶乘的现象,

n阶乘的规律是  n! = n * (n - 1)!  ,n的阶乘 等于 n乘以n减1的阶乘,然后再求n减1的阶乘n减1的阶乘等于n减1乘以n减2的阶乘,一直求到1结止,这才是它抽象的规律


函数就是为了抽象的规则,用函数把这个规则写出来

function mul(n){

    return n * mul(n - 1); // n乘以n减1的阶乘,把这个结果返回

}

mul(5);


理解一下 mul(5)         

mul(5) => 5 * mul(4)   5乘以4的阶乘的计算结果,要等到函数调用mul(5-1=4)的计算出结果乘5,mul(4)的结果又要等下面mul(3)的结果

mul(4) => 4 * mul(3)   4阶的乘调用函数mul(4-1=3)等到3的阶乘结果,接下来3的阶乘的结果,又要等2的阶乘的结果

mul(3) => 3 * mul(2)   3阶的乘的结果,要等2阶乘的结果 

mul(2) => 2 * mul(1)   2阶乘的结果,要等1阶乘的结果

                                    1的阶乘结果等0的阶乘,再等-1的一直往下等……


一直往下等算不到头,无限死循环下去,这个函数还是差最后一步找到它的"出口",什么时候让无限死循环终止返回具体的数呢?

找到已知的东西,5的阶乘算不出来,4的阶乘算不出来,0的阶乘是已知的,1的阶乘也是已知的一样的,1的阶乘就可以,也就是说当n是1的时候不用去计算了,返回结果就是1,已知1的阶乘结果就是1 


判断n等于1的时候 return 1; 就可以了,有了这句之后这个函数就发生质变了,原来还无限死循环,现在这个函数就有了一个出口

function mul(n){

    if(n == 1){
        return 1;
    }

    return n * mul(n - 1);
}

mul(5); // 120


/**
 * mul(5) ==> 5 * mul(4)
 * mul(4) ==> 4 * mul(3)
 * mul(3) ==> 3 * mul(2)
 * mul(2) ==> 2 * 1
 *
 * mul(5) ==> 5 * mul(4)
 * mul(4) ==> 4 * mul(3)
 * mul(3) ==> 3 * 2
 * mul(2) ==> 2 * 1
 *
 * mul(5) ==> 5 * mul(4)
 * mul(4) ==> 4 * 6
 * mul(3) ==> 3 * 2
 * mul(2) ==> 2 * 1

 * mul(5) ==> 5 * 24
 * mul(4) ==> 4 * 6
 * mul(3) ==> 3 * 2
 * mul(2) ==> 2 * 1
 *
 * mul(5) ==> 5 * 24 = 120
 * mul(4) ==> 4 * 6
 * mul(3) ==> 3 * 2
 * mul(2) ==> 2 * 1
 */

想知道5的阶乘的结果,必须先知道4的阶乘的结果,想知道4阶乘的结果必须知道3阶乘的结果,想知道3的结果必须知道2的结果,想知道2的结果必须知道1的结果,现在1的结果已经知道是1不需要计算了

mul(5) => 5 * mul(4)=>  5 x 24=120   mul(4)的结果变成24 ,这样一层一层往上反结果,最后5的阶乘的结果120就求出来了

mul(4) => 4 * mul(3) => 4 x = 24     mul(3)变成6,计算完mul(4)的结果24

mul(3) => 3 * mul(2) => 3 x = 6       mul(2)的结果2直接返回到这,mul(3)的结果就可求了    

mul(2) => 2 * mul(1) => 2 x = 2       现在已知1的阶乘mul(1)结果是1,到1这立马换成数,然后进行计算,计算完后是mul(2)的结果2

这个整体的过程叫用"递归"的方法来解决问题


递归主要关注两点

1. 先说一下递归的好处就是特别符合人为的想法,我们的思维过程是我想求n的阶乘,最直观的找到它的规律是"n的阶乘等于n乘以n-1的阶乘",

    然而我们把这条把这条规律 n * (n - 1) 写到程序里面了,递归就是更好找规律这是第一点。

    找规律,递归就是return公式,没有return就不会循环计算

2. 第二点找出口,递归没有出口就会无限死循环,必须用一个已知的数当做递归的出口,最后不用公式去计算,用一个实质性的数返回结果


程序有一个缺陷,求不了0的阶乘,把0的阶乘加上,0的阶乘是已知就是1

function mul(n){
  
    if(n == 1 || n == 0){

            return 1;

    }
    
    return n * mul(n - 1);

}

mul(0); // 1


递归只有一点好处,它能让代码变的更加简洁,除此以外没有任何好处,特别复杂的程序不能用递归它特别慢

比如求10的阶乘 mul(10) ,想知道10的阶乘要等9的阶乘算完,9的阶乘要等8的阶乘算完,一层一层的等,

等到最后1的阶乘算完之后往回反结果,1的阶乘知道了,2的阶乘马上知道了,然后3的阶乘知道了,最后才知道是10的阶乘,10的阶乘最先执行,10阶乘最后被执行完,所以递归慢,空间复杂度非常高。

image.png

但是递归的好处是,代码写完后少了不少工作量,下面的“斐波那契数列”就知道什么是真正的少了。


五、写一个函数,实现斐波那契数列

斐波那契数列正常用循环也能写出来,但是用递归就很容易找到抽象的概念了,对这种很容易找到抽象规律的,用递归是最方便的。

斐波那契数列的规律是 第n位 等于 n-1位 加上 n-2位的和 ,而且是有出口的"1或2",有规律有出口就大胆的用递归吧。

function fb(n){

    if(n == 1 || n == 2){
        return 1;
    }

    return fb(n - 1) + fb(n - 2); // 第n位等于 n-1位 加上 n-2位的和

}

console.log(fb(5)); // 5


/**
 * fb(5) ==>  fb(4) + fb(3);
 * fb(4) ==>  fb(3) + fb(2);   fb(3) ==>  fb(2)  +   fb(1);
 * fb(3) ==>  fb(2) + fb(1);

 * fb(5) ==>  fb(4) + fb(3);
 * fb(4) ==>  fb(3) + fb(2);   fb(3) ==>  fb(2) +   fb(1);
 * fb(3) ==>  1   +   1;

 * fb(5) ==>  fb(4) + fb(3);
 * fb(4) ==>  fb(3) + fb(2);   fb(3) ==>  1 +   1;
 * fb(3) ==>  1   +   1;

 * fb(5) ==>  fb(4) + fb(3);
 * fb(4) ==>  2 + 1;   fb(3) ==>  1 +   1;
 * fb(3) ==>  1   +   1;

 * fb(5) ==>  3 + 2;
 * fb(4) ==>  2 + 1;   fb(3) ==>  1 +   1;
 * fb(3) ==>  1   +   1;
 */

输入的数对应输出的斐波那契数

输入对应的数: 1  2  3  4  5  6  7   8   9   10  11...

斐波那契数列: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89...


六、自己做的一个练习,用递归的方法依次打印 10 - 1

依次打印10 ~ 1,规律是n - 1,出口是n等于1

function dg(n){

    if(n == 1){
        console.log(n);
        return ;
    }else{
        console.log(n);
    }
    
    return dg(n - 1);
}

dg(10);

把上面的代码优化一下,出口是n等于0

function mul(n){

    if(n == 0){
        //console.log(n);
        return ;
    }

    console.log(n);

    return mul(n - 1);

}

mul(10);

不写作业也是可以的,但总要练点什么东西,要么练练技术,要么练练心里素质



Leave a comment 0 Comments.

Leave a Reply

换一张