JS中的遞歸


 

 

遞歸基礎

遞歸的概念

  • 在程序中函數直接或間接調用自己
    1. 直接調用自己
    2. 簡介調用自己
  • 跳出結構,有了跳出才有結果

遞歸的思想

  • 遞歸的調用,最終還是要轉換為自己這個函數
    1. 如果有個函數foo,如果他是遞歸函數,到最后問題還是轉換為函數foo的形式
    2. 遞歸的思想就是將一個未知問題轉換為一個已解決的問題來實現
    function foo(){ ...foo(...)... }

遞歸的步驟(技巧)

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

簡單遞歸練習

求1-100的和

  • 分析:

    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; }
  2. 將臨界條件加入到遞歸中
    • 求100 轉換為 求99
    • 求99 轉換為 求98
    • 求98 轉換為 求97
    • ...
    • 求2 轉換為 求1
    • 求1 轉換為 求1
    • 即 sum(1) = 1
  3. 遞歸函數

    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); }
4. 臨界條件 * fib(0) == 1 * fib(1) == 1  5. 遞歸函數
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 開始.  * 分析: 1. 假設已知 foo(n) 為 1-n 的積 2. 遞歸關系  * foo(n) = foo(n-1) * n 3. 遞歸體
function foo(n){ return foo(n-1) * n }
4. 臨界條件  * foo(1) == 1 5. 遞歸函數
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 3. 遞歸體
function power(n,m){ return power(n,m-1) * n; }
4. 臨界條件  * m == 1 ,return n  * m == 0 ,reutnr 1 5. 遞歸函數
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); } } }

```


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM