1遞歸含義:在某時某刻某個條件下調用包含自己的函數
2:注意點:⑴遞歸過程中一定要加限制條件,要不然會陷入死循環:
死循環eg:
function f(someP){ f(somP); } f(4); //Uncaught RangeError: Maximum call stack size exceeded
正常調用:
//計算輸入某個正整數,然后一直向下疊加 ,這里僅僅做一個簡單示例,不進行輸入num的判斷
function f(num){ let x; if(num>0){ x =num + f(num-1); return x; } return false; } f(5);
⑵遞歸有個過程,不是一步到位的,這一點尤其重要,因為在學習JS數據結構與算法中的二叉搜索樹的移除代碼會至關重要,不懂遞歸過程的話很容易看不懂移除代碼
function getSum(num){ if(x === 1){ return 1; } return num + getSum(num-1) } getSum(5);
過程如下:
①:getSum(5)調用函數並傳入參數5,執行函數中的num +getSum(num-1) ->5+getSum(5-1)
②:getSum(4)調用函數並傳入參數4,執行函數中的num+getSum(num-1) ->4+getSum(4-1)
③:getSum(3)調用函數並傳入參數3,執行函數中的num+getSum(num-1) ->3+getSum(3-1)
④:getSum(2)調用函數並傳入參數2,執行函數中的num+getSum(num-1) ->2+getSum(2-1)
⑤:getSum(1)調用函數並傳入參數1,執行函數中的return 1;
⑥:這時候再一步一步往回走,即1+2+3+4+5;即可以理解為遞歸調用過程中,是不會立即計算的,要等到限制條件結束后,才會一步一步往上計算。
3:二叉搜索樹的移除節點:移除節點程序是二叉搜索樹程序方法中最復雜的一個。
eg:
class Node{ //節點類 constructor(key){ this.key = key; this.left = null; this.right = null; } } function defaultCompare(a, b) { //比較函數 if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; } const Compare = { LESS_THAN: -1, BIGGER_THAN: 1, EQUALS: 0 }; class BinarySearchTree{ constructor(compareFn = defaultCompare){ this.compareFn = compareFn; this.root = null; } remove(key){ this.root = this.removeNode(this.root,key); } removeNode(node,key){ if(node == null){ return null; } if(this.compareFn(key,node.key) === Compare.LESS_THAN){ // (1) node.left = this.removeNode(node.left,key); //(2) return node; //(3) }else if (this.compareFn(key,node.key) === Compare.BIGGER_THAN){ //(4) node.right = this.removeNode(node.right,key); //(5) return node; //(6) }else{ //(7) if(node.left == null && node.right == null){ //(8) //第一種情況,移除一個葉節點(只有父節點沒有子節點的節點) node = null; //(9) return node; //(10) } if(node.left == null){ //第二種情況,移除有一個左or右子節點的節點 node = node.right; //(11) return node; //(12) }else if(node.right == null) { node = node.left; //(13) return node; //(14) } const aux = this.minNode(node.right); //(15) //第三種情況,移除有兩個子節點的節點 node.key = aux.key; //(16) node.right = this.removeNode(node.right,aux.key); //(17) return node; //(18) } } }
const tree1 = new BinarySearchTree();
tree1.remove(8);
假設現在有一個節點順序為
現在我們需要移除節點8,則代碼的順序應該是:
①:開始時key:8 node: Node{ key: 11, left : Node, right : Node},進入行(1),判斷大小后進入行(2),此時key:8 node: Node{ key: 7, left : Node, right : Node}
②:遞歸調用第一次,進入行(4)判斷大小后進入行(5),此時key:8 node: Node{ key: 9, left : Node, right : Node}
③:遞歸調用第二次,進入行(1)判斷大小后進入行(2),此時key:8 node: Node{ key: 8, left : null, right : null}
④:遞歸調用第三次,進入行(7 ,8, 9),返回一個node,此時key: 8 ,node :null;
⑤:進入行(3),此時結果為: key:8 ,node:Node{key: 9 ,left:null,right:Node};
⑥:進入行(6),此時結果為: key:8 ,node:Node{key: 9 ,left:Node,right:Node};
⑦:進入行(3),此時結果為:key:8 ,node:Node{key: 11,left:Node,right:Node};
⑧:返回到remove()后,跳出程序,節點8已移除。
備注1:上述步驟只實現了第一種情況,如果有需要,讀者可以用chrome的調試工具進行斷點調試,在Sources中可查看key及node的具體情況,
備注2:這里很明顯說明了遞歸調用的執行順序,我就是因為不懂執行順序,這段代碼看了我2個多小時。。。。。切記切記,