JS中的遞歸

遞歸基礎

遞歸的概念

  • 在程序中函數直接或間接調用本身
    1. 直接調用本身
    2. 間接調用本身
  • 跳出結構,有了跳出纔有結果

遞歸的思想

  • 遞歸的調用,最終仍是要轉換爲本身這個函數
    1. 若是有個函數foo,若是他是遞歸函數,到最後問題仍是轉換爲函數foo的形式
    2. 遞歸的思想就是將一個未知問題轉換爲一個已解決的問題來實現
function foo(){
        ...foo(...)...
    }

遞歸的步驟(技巧)

1. 假設遞歸函數已經寫好
2. 尋找遞推關係
3. 將遞推關係的結構轉換爲遞歸體
4. 將臨界條件加入到遞歸體中

簡單遞歸練習

求1-100的和

  • 分析:html

    1. 假設遞歸函數已經寫好爲sum,既sum(100),就是求1-100的和
    2. 尋找遞推關係: 就是 n 與 n-1 ,或 n-2 之間的關係
      sum(n) == sum(n-1) + n
    var res = sum(100);
      var res = sum(99) + 100;
  1. 將遞歸結構轉換成遞歸體
function sum(n){
        return sum(n-1) + n;
    }
  1. 將臨界條件加入到遞歸中
    • 求100 轉換爲 求99
    • 求99 轉換爲 求98
    • 求98 轉換爲 求97
    • ...
    • 求2 轉換爲 求1
    • 求1 轉換爲 求1
    • 即 sum(1) = 1
  2. 遞歸函數
function sum(n){
        if(n==1) return 1;
        return sum(n-1) + n;
    }

求 1,3,5,7,9,...第n項的結果和前n項和,序號從0開始

  • 分析
    1. 假設遞歸函數已經完成foo(n),獲得奇數
    2. 遞歸關係:
      • foo(n) = foo(n-1)+2
    3. 遞歸體
    function foo(n){
          return foo(n) = sum(n-1)+2;
      }
  1. 跳出條件
    • foo(n) = foo(n-1) + 2
    • foo(1) = foo(0) + 2
    • foo(0) = 1;
  2. 遞歸函數
function foo(n){
        if(n == 0) return 1;
        return foo(n-1) + 2;
    }
* 前 n 項的和
* 分析
    1. 假設完成,sum(n)就是前n項的和
    2. 遞推關係
        * foo(n) = sum(n) + 第n-1項以前的和
    3. 遞歸體
function sum(n){
        return foo(n) + sum(n-1);
    }
4. 臨界條件
        * n == 1 ,結果爲1
    5. 遞歸函數
function foo(n){
        if(n == 0) return 1;
        return foo(n-1) + 2;

    }

    function sum(n){
        if(n == 0) return 1;
        return foo(n) + sum(n-1);
    }

求 2,4,6,8,10... 第n項與前n項之和

  • 分析
    1. 假設已知函數 fn(n)爲第n項,sum(n)爲前n項之和
    2. 遞歸關係
      • fn(n) = fn(n-1) + 2
      • sum(n) = fn(n) + sum(n-1)
    3. 遞歸體
function fn(n){
        return fn(n) = (n-1) + 2
    }
    function sum(n){
        return sum(n) = fn(n) + sum(n-1);
    }
  1. 臨界條件
    • fn(0) = 2
    • sum(0) = 2;
  2. 遞歸函數
function fn(n){
        if(n == 0) return 2;
        return fn(n-1) + 2;
    }
    function sum(n){
        if(n==0) return 2;
        return fn(n) + sum(n-1);
    }

數列 1,1,2,4,7,11,16...求第 n 項,求前n項和

  • 分析
    1. 假設已知函數 foo(n) 爲第n項
    2. 遞歸關係
      從第 0 項開始計算
      • 第 0 項, 1 => foo(0) + 0 = foo(1)
      • 第 1 項, 2 => foo(1) + 1 = foo(2)
      • 第 2 項, 3 => foo(2) + 2 = foo(3)
      • ...
      • 第 n-1 項, n => foo(n-1) + n-1 = foo(n)
      • foo(n) = foo(n-1) + n-1;
      從第 1 項開始計算
      • 第 1 項, 2 => fn( 1 ) + 0 = fn( 2 )
      • 第 2 項, 3 => fn( 2 ) + 1 = fn( 3 )
      • 第 3 項, 4 => fn( 3 ) + 2 = fn( 4 )
      • ...
      • foo(n) = fn(n-1) + n - 2
      • 若是從 0 開始
0  1  2  3  4  5   6
    1, 1, 2, 4, 7, 11, 16,
* 若是從 1 開始
1  2  3  4  5  6   7
    1, 1, 2, 4, 7, 11, 16
3. 遞歸體
function foo(n){
        return foo(n-1)+n-1;
    }
4. 臨界條件

    * foo(0) == 1;
    * foo(1) == 1;
5. 遞歸函數
function foo(n){
        if(n == 0) return 1;
        return foo(n-1) + n -1;
    }
* 分析

    1. 假設已知函數 sum(n)爲前n項和

    2. 遞歸關係
        * sum(n) = foo(n) + sum(n-1);

    3. 遞歸體
function sum(n){
        return foo(n) + sum(n-1);
    }
4. 臨界條件

        * sum(0) = 1;
    5. 遞歸函數
function sum(n){
        if(n == 0) return 1;
        return foo(n) + sum(n-1);
    }

Fibonacci數列(斐波那契數列)

1,1,2,3,5,8,13,21,34,55,89...求第 n 項
  • 分析
  1. 假設已知 fib(n) 爲第 n 項
  2. 遞歸關係
    • fib(n) = fib(n-1) + fib(n-2)
  3. 遞歸體
function fib(n){
        return fib(n-1)+fib(n-2);
    }
  1. 臨界條件
    • fib(0) == 1
    • fib(1) == 1
      1. 遞歸函數
      function fib(n){
         if(n == 0 || n ==1) return 1;
         return fib(n-1) + fib(n-2);
       }

高級遞歸練習

階乘

概念:
* 階乘是一個運算, 一個數字的階乘表示的是從 1 開始 累乘到這個數字.
* 例如 3! 表示 1 * 2 * 3. 5! 就是 1 * 2 * 3 * 4 * 5. 規定 0 沒有階乘,
* 階乘 從 1 開始.
* 分析:node

  1. 假設已知 foo(n) 爲 1-n 的積
  2. 遞歸關係
    * foo(n) = foo(n-1) * n
  3. 遞歸體
function foo(n){
        return foo(n-1) * n
    }
  1. 臨界條件
    * foo(1) == 1
  2. 遞歸函數
function foo(n){
        if( n == 1) return 1;
        return foo(n - 1) * n;
    }

求冪

  • 概念:
    求冪就是求 某一個數 幾回方
    2*2 2 的 平方, 2 的 2 次方
    求 n 的 m 次方
    最終要獲得一個函數 power( n, m )
    n 的 m 次方就是 m 個 n 相乘 即 n 乘以 (m-1) 個 n 相乘
  • 分析
  1. 假設已知函數 power(n,m) 爲 n 的 m 次冪
  2. 遞歸關係
  • power(n,m-1) * n
  1. 遞歸體
function power(n,m){
        return power(n,m-1) * n;
    }
  1. 臨界條件
    • m == 1 ,return n
    • m == 0 ,reutnr 1
  2. 遞歸函數
function power(n,m){
        if(m == 1) return n;
        return power(n,m-1) * n;
    }

深拷貝,使用遞歸方式

概念:數組

  1. 若是拷貝的時候, 將數據的全部引用結構都拷貝一份, 那麼數據在內存中獨立就是深拷貝(內存隔離,徹底獨立)
  2. 若是拷貝的時候, 只針對當前對象的屬性進行拷貝, 而屬性是引用類型這個不考慮, 那麼就是淺拷貝
  3. 拷貝: 複製一份. 指將對象數據複製.
  4. 在討論深拷與淺拷的時候必定要保證對象的屬性也是引用類型.
    實現方法:
  5. 若是要實現深拷貝那麼就須要考慮將對象的屬性, 與屬性的屬性,都拷貝過來
  6. 分析(2個參數,簡單實現)
    1. 假設已經實現 clone ( o1, o2),將對象 o2 的成員拷貝一份交給 o1
    2. 遞推關係
      • 混合方法,將 o2 的成員拷貝到 o1 中
      function clone( o1, o2){
          for(var key in o2){
              o1[key] = o2[key];
          }
      }
      • 假設方法已經實現,若是 o2[key] 是對象
      • 繼續使用這個方法
      • 須要考慮 o2[key] 是引用類型,再一次使用clone函數
      • 若是 o2[key] 不是引用類型,那麼直接賦值
    3. 臨界條件
      • 由於是 for in 循環,沒有成員遍歷時,自動結束
    4. 遞歸函數
    function clone(o1,o2){
         for(var key in o2){
             if(typeof o2[key] == 'object'){
                 o1[key] = {};
                 clone(o1[key],o2[key])
             }else{
                 o1[key] = o2[key];
             }
         }
     }

    複雜實現(一個參數)
    原理: clone(o) = new Object; 返回一個對象
    遞歸函數函數

    function clone(o){
         var temp = {};
         for(var key in o){
             if(typeof o[key] == 'object'){
                 temp[key] = clone(o[key]);
             }else{
                 temp[key] = o[key];
             }
         }
         return temp;
     }

使用遞歸實現 getElementsByClassName

html結構:
<div>
        <div>1
            <div class="c">2</div>
            <div>3</div>
        </div>
        <div class="c">4</div>
        <div>5
            <div>6</div>
            <div class="c">7</div>
        </div>
        <div>8</div>
    </div>
分析
    1. 實現一個方法byClass()須要的參數是:
        node: 在某個節點上尋找元素
        className: 須要尋找的className
        arr: 找到的元素存儲到這個數組中
    2. 遍歷 node 的子節點,
    3. 查看這個子節點是否還有子節點,若是沒有直接存儲到數組中,若是有就繼續遞歸
var arr = [];
    function byClass(node, className, arr){
        //獲得傳入節點的全部子節點
        var lists = node.childNodes;
        for(var i = 0;i< lists.length;i++){
            //判斷是否有相同className元素
            if(arr[i],className == className){
                arr.push(arr[i]);
            }
            //判斷子節點是否還有子節點
            if(arr[i].childNodes.length > 0){
                byClass(arr[i],className,arr);
            }
        }
    }
相關文章
相關標籤/搜索