遞歸基礎
遞歸的概念
- 在程序中函數直接或間接調用自己
- 直接調用自己
- 簡介調用自己
- 跳出結構,有了跳出才有結果
遞歸的思想
- 遞歸的調用,最終還是要轉換為自己這個函數
- 如果有個函數foo,如果他是遞歸函數,到最后問題還是轉換為函數foo的形式
- 遞歸的思想就是將一個未知問題轉換為一個已解決的問題來實現
function foo(){ ...foo(...)... }
遞歸的步驟(技巧)
1. 假設遞歸函數已經寫好 2. 尋找遞推關系 3. 將遞推關系的結構轉換為遞歸體 4. 將臨界條件加入到遞歸體中
簡單遞歸練習
求1-100的和
-
分析:
- 假設遞歸函數已經寫好為sum,既sum(100),就是求1-100的和
-
尋找遞推關系: 就是 n 與 n-1 ,或 n-2 之間的關系
sum(n) == sum(n-1) + nvar res = sum(100); var res = sum(99) + 100;
-
將遞歸結構轉換成遞歸體
function sum(n){ return sum(n-1) + n; }
- 將臨界條件加入到遞歸中
- 求100 轉換為 求99
- 求99 轉換為 求98
- 求98 轉換為 求97
- ...
- 求2 轉換為 求1
- 求1 轉換為 求1
- 即 sum(1) = 1
-
遞歸函數
function sum(n){ if(n==1) return 1; return sum(n-1) + n; }
求 1,3,5,7,9,...第n項的結果和前n項和,序號從0開始
- 分析
- 假設遞歸函數已經完成foo(n),得到奇數
- 遞歸關系:
- foo(n) = foo(n-1)+2
-
遞歸體
function foo(n){ return foo(n) = sum(n-1)+2; }
- 跳出條件
- foo(n) = foo(n-1) + 2
- foo(1) = foo(0) + 2
- foo(0) = 1;
-
遞歸函數
function foo(n){ if(n == 0) return 1; return foo(n-1) + 2; }
- 前 n 項的和
- 分析
- 假設完成,sum(n)就是前n項的和
- 遞推關系
- foo(n) = sum(n) + 第n-1項之前的和
-
遞歸體
function sum(n){ return foo(n) + sum(n-1); }
- 臨界條件
- n == 1 ,結果為1
-
遞歸函數
```
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項之和
- 分析
- 假設已知函數 fn(n)為第n項,sum(n)為前n項之和
- 遞歸關系
- fn(n) = fn(n-1) + 2
- sum(n) = fn(n) + sum(n-1)
- 遞歸體
function fn(n){ return fn(n) = (n-1) + 2 } function sum(n){ return sum(n) = fn(n) + sum(n-1); }
- 臨界條件
- fn(0) = 2
- sum(0) = 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); } } }
```