什么是递归

递归

什么是递归?

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

  1. 直接调用自己
  2. 间接调用自己

就递归而言最重要的就是跳出结构. 因为跳出了才可以有结果.

所谓的递归就是化归思想

递归的调用, 写递归函数, 最终还是要转换为自己这个函数.

假如有一个函数 f, 如果它是递归函数的话, 那么也就是说 函数体内的问题还是转换为 f 的形式.

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

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

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

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

posted @ 2016-08-08 00:10  笑ぅ陌生  阅读(133)  评论(0编辑  收藏  举报