手记

javascript递归算法的初步认识及使用技巧

递归

什么是递归?

在程序中,所谓的递归就是函数直接调用自己或者间接调用自己

递归思想就是将一个问题转换为一个已解决的问题

理论就不多说了,这些理论我也看得不是太懂,直接上例子:

1.求 1+2+3+...+100的和

    分析:
    1. 首先假设递归函数已经写好了,假设为 sum,那么sum(100)就是求从1加到100的和
    2. 找出递归关系  sum(100) == sum(99) + 100;
    3. 写出递归体 function sum(n){
                    return sum(n-1) + n
                 }
    4. 将临界条件加到递归体中
        * 将 求 100 转换为 求 99
        * 将 求 99 转换为 求 98
        * ...
        * 将求 2 转换为 求 1
        * 求 1 结果就是 1
        * 即: sum( 1 ) 是 1
        function sum(n){
            if ( n==1 ) return 1; 
            return sum(n-1) + n
        }
function sum(n){
    if ( n==1 ) return 1; 
    return sum(n-1) + n
} 
sum(100)

提升一下难度
2.求数列: 1, 1, 2, 4, 7, 11, 16, … 求 第 n 项, 求前 n 项和.

分析:
其实分析的方法都是一样的,只不过这一题找递归关系可能难一点,我们可以列出几项找出关系
1. 假设这个函数已经存在为fn
2. 找出递推关系 f(2) = f(1) + 0;
               f(3) = f(2) + 1;
               f(4) = f(3) + 2;
               ... 
               f(n) = f(n-1) + n - 2;
接下来的步骤都是一样的了,我就不再叙述
    function fn(n){
        if ( n==1 ) return 1;
        return fn(n-1) + n-2;
    }
    function sum(n){
        if ( n == 1 ) return 1;
        return sum(n-1) + fn(n)
    }
    console.log(fn(5)+","+sum(5));

递归高级

用递归实现深拷贝
什么是深拷贝?
就是在拷贝数据的时候,将数据的所有引用结构都拷贝一份。简单的说就是在内存中存在两个数据结构完全相同的有相互独立的数据。

分析:
1.首先假设深拷贝这个方法已经完成,为deepClone
2.要拷贝一个数据,我们肯定要去遍历它的属性,如果这个对象的属性仍是对象,继续使用这个方法,如此往复
**方法一**
function deepClone1(o1,o2){
    for (var k in o2){
        if (typeof o2[k] === "object"){
            o1[k] = {};
            deepClone1(o1[k],o2[k]);
        }  else{
            o1[k] = o2[k];
        }
    }
}

**方法二**
function deepClone2(obj){
    var temp = {};
    for( var k in obj ){
        if ( typeof obj[k] === "object" ){
            temp[k] = deepClone2(obj[k]);
        } else {
            temp[k] = obj[k];
        }
    } 
}

递归的性能问题

使用递归可以帮我们简化一些算法问题,但其性能问题也是我们补课忽略的。

什么也不多说,直接上代码,以斐波那契数列为例

求兔子数列 1,1,2,3,5,8,13,21,34,55,89...中的第n项

不难看出这个例子中的递推关系是后一项等于前两项的和
fn(n) = fn( n-1 ) + fn( n-2 );
    定义一个计数变量count,看再求出第n项,这个函数被调用了多少次
    var count = 0;
    function fn(n){
        count++;
        if( n==1  n==2){ return 1; }
        return fn(n-1) + fn(n-2);
    }
    // n = 5 => count = 9
    // n = 6 => count = 15
    // n = 7 => count = 25
    // n = 8 => count = 41
    ...
    // n = 20 => count = 13529
    // n = 21 => count = 21891
由此可看出当你计算越大的值时,函数被调用的次数也就越多  

画张图看下

由此可看出,性能低的原因是重复计算。因此我们可以将每次的计算结果记录下来。
每次计算的时候我们可以先看看数据是否存储起来,如果有直接拿来用,如果没有在进行递归,但递归后需要将计算结果

性能优化
var data = [1,1];
function feb(n){
    var v = data[n];
    if ( data[n] === undefined ){
        v = feb(n-1) + feb(n-2);
        // 将计算出的结果存储到数组中
        data[n] = v;
    }
    return v;
}
// n = 10 => count = 19
// n = 30 => count = 15
// n = 21 => count = 43

由此可看出如果将重复计算的值记录下来,性能会得到大大的优化

以上,我对递归的初步学习,欢迎大家前来一起讨论。

11人推荐
随时随地看视频
慕课网APP

热门评论

got it 浅显易懂

got it,很容易懂


hello world


查看全部评论