樹型結構是一類很是重要的非線性結構。直觀地,樹型結構是以分支關係定義的層次結構。node
樹在計算機領域中也有着普遍的應用,例如在編譯程序中,用樹來表示源程序的語法結構;在數據庫系統中,可用樹來組織信息;在分析算法的行爲時,可用樹來描述其執行過程等等。git
下面講解的內容完整代碼在這:https://github.com/LukeLin/data-structure-with-js/blob/master/Binary%20tree/BinaryTree.jsgithub
首先看看樹的一些概念:算法
1.樹(Tree)是n(n>=0)個結點的有限集。在任意一棵非空樹中:數據庫
(1)有且僅有一個特定的稱爲根(Root)的結點;數組
(2)當n>1時,其他結點可分爲m(m>0)個互不相交的有限集T1,T2,T3,...Tm,其中每個集合自己又是一棵樹,而且稱爲根的子樹(Subtree)。ide
例如,(a)是隻有一個根結點的樹;(b)是有13個結點的樹,其中A是根,其他結點分紅3個互不相交的子集:T1={B,E,F,K,L},t2={D,H,I,J,M};T1,T2和T3都是根A的子樹,且自己也是一棵樹。post
2.樹的結點包含一個數據元素及若干指向其子樹的分支。結點擁有的子樹數稱爲結點的度(Degree)。例如,(b)中A的度爲3,C的度爲1,F的度爲0.度爲0的結點稱爲葉子(Leaf)或者終端結點。度不爲0的結點稱爲非終端結點或分支結點。樹的度是樹內各結點的度的最大值。(b)的樹的度爲3.結點的子樹的根稱爲該結點的孩子(Child)。相應的,該結點稱爲孩子的雙親(Parent)。同一個雙親的孩子之間互稱兄弟(Sibling)。結點的祖先是從根到該結點所經分支上的全部結點。反之,以某結點爲根的子樹中的任一結點都稱爲該結點的子孫。ui
3.結點的層次(Level)從根開始定義起,根爲第一層,跟的孩子爲第二層。若某結點在第l層,則其子樹的根就在第l+1層。其雙親在同一層的結點互爲堂兄弟。例如,結點G與E,F,H,I,J互爲堂兄弟。樹中結點的最大層次稱爲樹的深度(Depth)或高度。(b)的樹的深度爲4。this
4.若是將樹中結點的各子樹當作從左至右是有次序的(即不能交換),則稱該樹爲有序樹,不然稱爲無序樹。在有序樹中最左邊的子樹的根稱爲第一個孩子,最右邊的稱爲最後一個孩子。
5.森林(Forest)是m(m>=0)棵互不相交的樹的集合。對樹中每一個結點而言,其子樹的集合即爲森林。
接下來看看二叉樹相關的概念:
二叉樹(Binary Tree)是另外一種樹型結構,它的特色是每一個結點至多隻有兩棵子樹(即二叉樹中不存在度大於2的結點),而且,二叉樹的子樹有左右之分(其次序不能任意顛倒。)
二叉樹的性質:
1.在二叉樹的第i層上至多有2的i-1次方個結點(i>=1)。
2.深度爲k的二叉樹至多有2的k次方-1個結點,(k>=1)。
3.對任何一棵二叉樹T,若是其終端結點數爲n0,度爲2的結點數爲n2,則n0 = n2 + 1;
一棵深度爲k且有2的k次方-1個結點的二叉樹稱爲滿二叉樹。
深度爲k的,有n個結點的二叉樹,當且僅當其每個結點都與深度爲k的滿二叉樹中編號從1至n的結點一一對應時,稱之爲徹底二叉樹。
下面是徹底二叉樹的兩個特性:
4.具備n個結點的徹底二叉樹的深度爲Math.floor(log 2 n) + 1
5.若是對一棵有n個結點的徹底二叉樹(其深度爲Math.floor(log 2 n) + 1)的結點按層序編號(從第1層到第Math.floor(2 n) + 1,每層從左到右),則對任一結點(1<=i<=n)有:
(1)若是i=1,則結點i、是二叉樹的根,無雙親;若是i>1,則其雙親parent(i)是結點Math.floor(i/2)。
(2)若是2i > n,則結點i無左孩子(結點i爲葉子結點);不然其左孩子LChild(i)是結點2i.
(3)若是2i + 1 > n,則結點i無右孩子;不然其右孩子RChild(i)是結點2i + 1;
二叉樹的存儲結構
1.順序存儲結構
用一組連續的存儲單元依次自上而下,自左至右存儲徹底二叉樹上的結點元素,即將二叉樹上編號爲i的結點元素存儲在加上定義的一維數組中下標爲i-1的份量中。「0」表示不存在此結點。這種順序存儲結構僅適用於徹底二叉樹。
由於,在最壞狀況下,一個深度爲k且只有k個結點的單支樹(樹中不存在度爲2的結點)卻須要長度爲2的n次方-1的一維數組。
2.鏈式存儲結構
二叉樹的結點由一個數據元素和分別指向其左右子樹的兩個分支構成,則表示二叉樹的鏈表中的結點至少包含三個域:數據域和左右指針域。有時,爲了便於找到結點的雙親,則還可在結點結構中增長一個指向其雙親結點的指針域。利用這兩種結構所得的二叉樹的存儲結構分別稱之爲二叉鏈表和三叉鏈表。
在含有n個結點的二叉鏈表中有n+1個空鏈域,咱們能夠利用這些空鏈域存儲其餘有用信息,從而獲得另外一種鏈式存儲結構---線索鏈表。
二叉樹的遍歷主要分三種:
先(根)序遍歷:根左右
中(根)序遍歷:左根右
後(根)序遍歷:左右根
二叉樹的順序存儲結構:
二叉樹的鏈式存儲形式:
// 順序存儲結構 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; // 鏈式存儲結構 function BinaryTree(data, leftChild, rightChild) { this.data = data || null; // 左右孩子結點 this.leftChild = leftChild || null; this.rightChild = rightChild || null; }
遍歷二叉樹(Traversing Binary Tree):是指按指定的規律對二叉樹中的每一個結點訪問一次且僅訪問一次。
1.先序遍歷二叉樹
1)算法的遞歸定義是:
若二叉樹爲空,則遍歷結束;不然
⑴ 訪問根結點;
⑵ 先序遍歷左子樹(遞歸調用本算法);
⑶ 先序遍歷右子樹(遞歸調用本算法)。
算法實現:
// 順序存儲結構的遞歸先序遍歷 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; console.log('preOrder:'); void function preOrderTraverse(x, visit) { visit(tree[x]); if (tree[2 * x + 1]) preOrderTraverse(2 * x + 1, visit); if (tree[2 * x + 2]) preOrderTraverse(2 * x + 2, visit); }(0, function (value) { console.log(value); }); // 鏈式存儲結構的遞歸先序遍歷 BinaryTree.prototype.preOrderTraverse = function preOrderTraverse(visit) { visit(this.data); if (this.leftChild) preOrderTraverse.call(this.leftChild, visit); if (this.rightChild) preOrderTraverse.call(this.rightChild, visit); };
2)非遞歸算法:
設T是指向二叉樹根結點的變量,非遞歸算法是: 若二叉樹爲空,則返回;不然,令p=T;
(1) p爲根結點;
(2) 若p不爲空或者棧不爲空;
(3) 若p不爲空,訪問p所指向的結點, p進棧, p = p.leftChild,訪問左子樹;
(4) 不然;退棧到p,而後p = p.rightChild, 訪問右子樹
(5) 轉(2),直到棧空爲止。
代碼實現:
// 鏈式存儲的非遞歸先序遍歷 // 方法1 BinaryTree.prototype.preOrder_stack = function (visit) { var stack = new Stack(); stack.push(this); while (stack.top) { var p; // 向左走到盡頭 while ((p = stack.peek())) { p.data && visit(p.data); stack.push(p.leftChild); } stack.pop(); if (stack.top) { p = stack.pop(); stack.push(p.rightChild); } } }; // 方法2 BinaryTree.prototype.preOrder_stack2 = function (visit) { var stack = new Stack(); var p = this; while (p || stack.top) { if (p) { stack.push(p); p.data && visit(p.data); p = p.leftChild; } else { p = stack.pop(); p = p.rightChild; } } };
2.中序遍歷二叉樹:
1)算法的遞歸定義是:
若二叉樹爲空,則遍歷結束;不然
⑴ 中序遍歷左子樹(遞歸調用本算法);
⑵ 訪問根結點;
⑶ 中序遍歷右子樹(遞歸調用本算法)。
// 順序存儲結構的遞歸中序遍歷 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; console.log('inOrder:'); void function inOrderTraverse(x, visit) { if (tree[2 * x + 1]) inOrderTraverse(2 * x + 1, visit); visit(tree[x]); if (tree[2 * x + 2]) inOrderTraverse(2 * x + 2, visit); }(0, function (value) { console.log(value); }); // 鏈式存儲的遞歸中序遍歷 BinaryTree.prototype.inPrderTraverse = function inPrderTraverse(visit) { if (this.leftChild) inPrderTraverse.call(this.leftChild, visit); visit(this.data); if (this.rightChild) inPrderTraverse.call(this.rightChild, visit); };
2) 非遞歸算法
T是指向二叉樹根結點的變量,非遞歸算法是: 若二叉樹爲空,則返回;不然,令p=T
⑴ 若p不爲空,p進棧, p=p.leftChild ;
⑵ 不然(即p爲空),退棧到p,訪問p所指向的結點,p=p.rightChild ;
⑶ 轉(1);
直到棧空爲止。
// 方法1 inOrder_stack1: function (visit) { var stack = new Stack(); stack.push(this); while (stack.top) { var p; // 向左走到盡頭 while ((p = stack.peek())) { stack.push(p.leftChild); } stack.pop(); if (stack.top) { p = stack.pop(); p.data && visit(p.data); stack.push(p.rightChild); } } }, // 方法2 inOrder_stack2: function (visit) { var stack = new Stack(); var p = this; while (p || stack.top) { if (p) { stack.push(p); p = p.leftChild; } else { p = stack.pop(); p.data && visit(p.data); p = p.rightChild; } } },
3.後序遍歷二叉樹:
1)遞歸算法
若二叉樹爲空,則遍歷結束;不然
⑴ 後序遍歷左子樹(遞歸調用本算法);
⑵ 後序遍歷右子樹(遞歸調用本算法) ;
⑶ 訪問根結點 。
// 順序存儲結構的遞歸後序遍歷 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; console.log('postOrder:'); void function postOrderTraverse(x, visit) { if (tree[2 * x + 1]) postOrderTraverse(2 * x + 1, visit); if (tree[2 * x + 2]) postOrderTraverse(2 * x + 2, visit); visit(tree[x]); }(0, function (value) { console.log(value); }); // 鏈式存儲的遞歸後序遍歷 BinaryTree.prototype.postOrderTraverse = function postOrderTraverse(visit) { if (this.leftChild) postOrderTraverse.call(this.leftChild, visit); if (this.rightChild) postOrderTraverse.call(this.rightChild, visit); visit(this.data); };
2) 非遞歸算法
在後序遍歷中,根結點是最後被訪問的。所以,在遍歷過程當中,當搜索指針指向某一根結點時,不能當即訪問,而要先遍歷其左子樹,此時根結點進棧。當其左子樹遍歷完後再搜索到該根結點時,仍是不能訪問,還需遍歷其右子樹。因此,此根結點還需再次進棧,當其右子樹遍歷完後再退棧到到該根結點時,才能被訪問。 所以,設立一個狀態標誌變量mark:
mark=0表示剛剛訪問此結點,mark=1表示左子樹處理結束返回,
mark=2表示右子樹處理結束返回。每次根據棧頂的mark域決定作何動做
算法實現思路:
(1) 根結點入棧,且mark = 0;
(2) 若棧不爲空,出棧到node;
(3) 若node的mark = 0,修改當前node的mark爲1,左子樹入棧;
(4) 若node的mark = 1,修改當前node的mark爲2,右子樹入棧;
(5) 若node的mark = 2,訪問當前node結點的值;
(6) 直到棧爲空結束。
postOrder_stack: function (visit) { var stack = new Stack(); stack.push([this, 0]); while (stack.top) { var a = stack.pop(); var node = a[0]; switch (a[1]) { case 0: stack.push([node, 1]); // 修改mark域 if (node.leftChild) stack.push([node.leftChild, 0]); // 訪問左子樹 break; case 1: stack.push([node, 2]); if (node.rightChild) stack.push([node.rightChild, 0]); break; case 2: node.data && visit(node.data); break; default: break; } } }
下面是完整代碼,其中包括二叉樹的遍歷和基本操做:
1 // 順序存儲結構 2 (function () { 3 // 順序存儲結構的遍歷 4 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; 5 6 console.log('preOrder:'); 7 void function preOrderTraverse(x, visit) { 8 visit(tree[x]); 9 if (tree[2 * x + 1]) preOrderTraverse(2 * x + 1, visit); 10 if (tree[2 * x + 2]) preOrderTraverse(2 * x + 2, visit); 11 }(0, function (value) { 12 console.log(value); 13 }); 14 15 console.log('inOrder:'); 16 void function inOrderTraverse(x, visit) { 17 if (tree[2 * x + 1]) inOrderTraverse(2 * x + 1, visit); 18 visit(tree[x]); 19 if (tree[2 * x + 2]) inOrderTraverse(2 * x + 2, visit); 20 }(0, function (value) { 21 console.log(value); 22 }); 23 24 console.log('postOrder:'); 25 void function postOrderTraverse(x, visit) { 26 if (tree[2 * x + 1]) postOrderTraverse(2 * x + 1, visit); 27 if (tree[2 * x + 2]) postOrderTraverse(2 * x + 2, visit); 28 visit(tree[x]); 29 }(0, function (value) { 30 console.log(value); 31 }); 32 }()); 33 34 var Stack = require('../Stack/stack'); 35 var Queue = require('../Queue/Queue').Queue; 36 37 // 鏈式存儲結構 38 function BinaryTree(data, leftChild, rightChild) { 39 this.data = data || null; 40 // 左右孩子結點 41 this.leftChild = leftChild || null; 42 this.rightChild = rightChild || null; 43 } 44 exports.BinaryTree = BinaryTree; 45 BinaryTree.prototype = { 46 constructor: BinaryTree, 47 // 判斷兩棵樹是否類似 48 isSimilar: function isSimilar(tree) { 49 return tree && 50 this.leftChild && isSimilar.call(this.leftChild, tree.leftChild) && 51 this.rightChild && isSimilar.call(this.rightChild, tree.rightChild); 52 }, 53 createBinaryTree: function (tree) { 54 void function preOrderTraverse(node, x, visit) { 55 visit(node, tree[x]); 56 57 if (tree[2 * x + 1]) preOrderTraverse(node.leftChild = new BinaryTree(), 2 * x + 1, visit); 58 if (tree[2 * x + 2]) preOrderTraverse(node.rightChild = new BinaryTree(), 2 * x + 2, visit); 59 }(this, 0, function (node, value) { 60 node.data = value; 61 }); 62 }, 63 64 // 先序遍歷二叉樹的非遞歸算法 65 preOrder_stack: function (visit) { 66 var stack = new Stack(); 67 stack.push(this); 68 69 while (stack.top) { 70 var p; 71 // 向左走到盡頭 72 while ((p = stack.peek())) { 73 p.data && visit(p.data); 74 stack.push(p.leftChild); 75 } 76 77 stack.pop(); 78 79 if (stack.top) { 80 p = stack.pop(); 81 stack.push(p.rightChild); 82 } 83 } 84 }, 85 preOrder_stack2: function (visit) { 86 var stack = new Stack(); 87 var p = this; 88 89 while (p || stack.top) { 90 if (p) { 91 stack.push(p); 92 p.data && visit(p.data); 93 p = p.leftChild; 94 } else { 95 p = stack.pop(); 96 p = p.rightChild; 97 } 98 } 99 }, 100 inOrder_stack1: function (visit) { 101 var stack = new Stack(); 102 stack.push(this); 103 104 while (stack.top) { 105 var p; 106 // 向左走到盡頭 107 while ((p = stack.peek())) { 108 stack.push(p.leftChild); 109 } 110 111 stack.pop(); 112 113 if (stack.top) { 114 p = stack.pop(); 115 p.data && visit(p.data); 116 stack.push(p.rightChild); 117 } 118 } 119 }, 120 inOrder_stack2: function (visit) { 121 var stack = new Stack(); 122 var p = this; 123 124 while (p || stack.top) { 125 if (p) { 126 stack.push(p); 127 p = p.leftChild; 128 } else { 129 p = stack.pop(); 130 p.data && visit(p.data); 131 p = p.rightChild; 132 } 133 } 134 }, 135 // 爲了區分兩次過棧的不一樣處理方式,在堆棧中增長一個mark域, 136 // mark=0表示剛剛訪問此結點,mark=1表示左子樹處理結束返回, 137 // mark=2表示右子樹處理結束返回。每次根據棧頂的mark域決定作何動做 138 postOrder_stack: function (visit) { 139 var stack = new Stack(); 140 stack.push([this, 0]); 141 142 while (stack.top) { 143 var a = stack.pop(); 144 var node = a[0]; 145 146 switch (a[1]) { 147 case 0: 148 stack.push([node, 1]); // 修改mark域 149 if (node.leftChild) stack.push([node.leftChild, 0]); // 訪問左子樹 150 break; 151 case 1: 152 stack.push([node, 2]); 153 if (node.rightChild) stack.push([node.rightChild, 0]); 154 break; 155 case 2: 156 node.data && visit(node.data); 157 break; 158 default: 159 break; 160 } 161 } 162 }, 163 164 preOrderTraverse: function preOrderTraverse(visit) { 165 visit(this.data); 166 if (this.leftChild) preOrderTraverse.call(this.leftChild, visit); 167 if (this.rightChild) preOrderTraverse.call(this.rightChild, visit); 168 }, 169 inPrderTraverse: function inPrderTraverse(visit) { 170 if (this.leftChild) inPrderTraverse.call(this.leftChild, visit); 171 visit(this.data); 172 if (this.rightChild) inPrderTraverse.call(this.rightChild, visit); 173 }, 174 postOrderTraverse: function postOrderTraverse(visit) { 175 if (this.leftChild) postOrderTraverse.call(this.leftChild, visit); 176 if (this.rightChild) postOrderTraverse.call(this.rightChild, visit); 177 visit(this.data); 178 }, 179 180 levelOrderTraverse: function (visit) { 181 var queue = new Queue(); 182 queue.enQueue(this); 183 184 while (queue.rear) { 185 var p = queue.deQueue(); 186 p.data && visit(p.data); 187 p.leftChild && queue.enQueue(p.leftChild); 188 p.rightChild && queue.enQueue(p.rightChild); 189 } 190 }, 191 // 求先序序列爲k的結點的值 192 getPreSequence: function (k) { 193 var count = 0; 194 195 void function recurse(node) { 196 if (node) { 197 if (++count === k) { 198 console.log('Value is: ' + node.data); 199 } else { 200 recurse(node.leftChild); 201 recurse(node.rightChild); 202 } 203 } 204 }(this); 205 }, 206 // 求二叉樹中葉子結點的數目 207 countLeaves: function () { 208 return function recurse(node) { 209 if (!node) return 0; 210 else if (!node.leftChild && !node.rightChild) return 1; 211 else return recurse(node.leftChild) + recurse(node.rightChild); 212 }(this); 213 }, 214 // 交換全部結點的左右子樹 215 revoluteBinaryTree: function revoluteBinaryTree() { 216 var temp = this.leftChild; 217 this.leftChild = this.rightChild; 218 this.rightChild = temp; 219 220 if (this.leftChild) revoluteBinaryTree.call(this.leftChild); 221 if (this.rightChild) revoluteBinaryTree.call(this.rightChild); 222 }, 223 // 求二叉樹中以值爲x的結點爲根的子樹深度 224 getSubDepth: function getSubDepth(x) { 225 if (this.data === x) { 226 console.log('subDepth: ' + this.getDepth()); 227 } else { 228 if (this.leftChild) getSubDepth.call(this.leftChild, x); 229 if (this.rightChild) getSubDepth.call(this.rightChild, x); 230 } 231 }, 232 getDepth: function getDepth() { 233 if (this === global) return 0; 234 else { 235 var m = getDepth.call(this.leftChild); 236 var n = getDepth.call(this.rightChild); 237 return (m > n ? m : n) + 1; 238 } 239 }, 240 // 刪除全部以元素x爲根的子樹 241 delSubX: function delSubX(x) { 242 if (this.data === x) { 243 this.leftChild = null; 244 this.rightChild = null; 245 } else { 246 if (this.leftChild) delSubX.call(this.leftChild); 247 if (this.rightChild) delSubX.call(this.rightChild); 248 } 249 }, 250 // 非遞歸複製二叉樹 251 copyBinaryTree_stack: function () { 252 // 用來存放本體結點的棧 253 var stack1 = new Stack(); 254 // 用來存放新二叉樹結點的棧 255 var stack2 = new Stack(); 256 stack1.push(this); 257 var newTree = new BinaryTree(); 258 var q = newTree; 259 stack2.push(newTree); 260 var p; 261 262 while (stack1.top) { 263 // 向左走到盡頭 264 while ((p = stack1.peek())) { 265 if (p.leftChild) q.leftChild = new BinaryTree(); 266 q = q.leftChild; 267 stack1.push(p.leftChild); 268 stack2.push(q); 269 } 270 271 p = stack1.pop(); 272 q = stack2.pop(); 273 274 if (stack1.top) { 275 p = stack1.pop(); 276 q = stack2.pop(); 277 if (p.rightChild) q.rightChild = new BinaryTree(); 278 q.data = p.data; 279 q = q.rightChild; 280 stack1.push(p.rightChild); // 向右一步 281 stack2.push(q); 282 } 283 } 284 285 return newTree; 286 }, 287 // 求二叉樹中結點p和q的最近祖先 288 findNearAncient: function (pNode, qNode) { 289 var pathP = []; 290 var pathQ = []; 291 findPath(this, pNode, pathP, 0); 292 findPath(this, qNode, pathQ, 0); 293 294 for (var i = 0; pathP[i] == pathQ[i] && pathP[i]; i++); 295 return pathP[--i]; 296 }, 297 toString: function () { 298 }, 299 // 求一棵二叉樹的繁茂度 300 lushDegree: function () { 301 var countArr = []; 302 var queue = new Queue(); 303 queue.enQueue({ 304 node: this, 305 layer: 0 306 }); 307 // 利用層序遍從來統計各層的結點數 308 var r; 309 while (queue.rear) { 310 r = queue.deQueue(); 311 countArr[r.layer] = (countArr[r.layer] || 0) + 1; 312 313 if (r.node.leftChild) 314 queue.enQueue({ 315 node: r.node.leftChild, 316 layer: r.layer + 1 317 }); 318 if (r.node.rightChild) 319 queue.enQueue({ 320 node: r.node.rightChild, 321 layer: r.layer + 1 322 }); 323 } 324 325 // 最後一個隊列元素所在層就是樹的高度 326 var height = r.layer; 327 for (var max = countArr[0], i = 1; countArr[i]; i++) 328 // 求層最大結點數 329 if (countArr[i] > max) max = countArr[i]; 330 331 return height * max; 332 }, 333 // 求深度等於書的高度減一的最靠左的結點 334 printPath_maxDepthS1: function () { 335 var maxh = this.getDepth(); 336 var path = []; 337 338 if (maxh < 2) return false; 339 find_h(this, 1); 340 341 function find_h(tree, h) { 342 path[h] = tree; 343 344 if (h == maxh - 1) { 345 var s = ' '; 346 for (var i = 1; path[i]; i++) s += path[i].data + (path[i + 1] ? ' -> ' : ''); 347 console.log(s); 348 return; 349 } else { 350 if (tree.leftChild) find_h(tree.leftChild, h + 1); 351 if (tree.rightChild) find_h(tree.rightChild, h + 1); 352 } 353 354 path[h] = null; 355 } 356 }, 357 // 求樹結點的子孫總數填入descNum域中,並返回 358 descNum: function () { 359 return function recurse(node) { 360 var d; 361 if (!node) return -1; 362 else d = recurse(node.leftChild) + recurse(node.rightChild) + 2; 363 364 node.descNum = d; 365 366 return d; 367 }(this); 368 } 369 }; 370 371 // 判斷二叉樹是否徹底二叉樹 372 BinaryTree.isFullBinaryTree = function (tree) { 373 var queue = new Queue(); 374 var flag = 0; 375 queue.enQueue(tree); 376 377 while (queue.rear) { 378 var p = queue.deQueue(); 379 380 if (!p) flag = 1; 381 else if (flag) return false; 382 else { 383 queue.enQueue(p.leftChild); 384 queue.enQueue(p.rightChild); 385 } 386 } 387 388 return true; 389 }; 390 391 // 求從tree到node結點路徑的遞歸算法 392 function findPath(tree, node, path, i) { 393 var found = false; 394 395 void function recurse(tree, i) { 396 if (tree == node) { 397 found = true; 398 return; 399 } 400 401 path[i] = tree; 402 if (tree.leftChild) recurse(tree.leftChild, i + 1); 403 if (tree.rightChild && !found) recurse(tree.rightChild, i + 1); 404 if (!found) path[i] = null; 405 }(tree, i); 406 } 407 408 var global = Function('return this;')(); 409 410 void function test() { 411 var tree = [1, 2, 3, 4, 5, , 6, , , 7]; 412 var test = new BinaryTree; 413 test.createBinaryTree(tree); 414 test.preOrderTraverse(function (value) { 415 console.log('preOrder: ' + value); 416 }); 417 test.inPrderTraverse(function (value) { 418 console.log('inOrder: ' + value); 419 }); 420 test.postOrderTraverse(function (value) { 421 console.log('postOrder: ' + value); 422 }); 423 test.preOrder_stack(function (data) { 424 console.log('preOrderNonRecusive: ' + data); 425 }); 426 test.preOrder_stack2(function (data) { 427 console.log('preOrder_stack2: ' + data); 428 }); 429 test.inOrder_stack1(function (value) { 430 console.log('inOrder_stack1: ' + value); 431 }); 432 test.inOrder_stack2(function (value) { 433 console.log('inOrder_stack2: ' + value); 434 }); 435 test.postOrder_stack(function (value) { 436 console.log('postOrder_stack: ' + value); 437 }); 438 test.getPreSequence(5); 439 console.log(test.countLeaves()); 440 test.getSubDepth(6); // 1 441 test.getSubDepth(2); // 3 442 test.levelOrderTraverse(function (value) { 443 console.log('levelOrderTraverse: ' + value); 444 }); 445 446 var newTree = test.copyBinaryTree_stack(); 447 448 var node1 = test.leftChild.leftChild; // 4 449 var node2 = test.leftChild.rightChild.leftChild; // 7 450 var ancient = test.findNearAncient(node1, node2); 451 console.log(ancient); 452 453 console.log('expect false: ' + BinaryTree.isFullBinaryTree(test)); 454 newTree.rightChild.leftChild = new BinaryTree(7); 455 newTree.leftChild.rightChild.leftChild = null; 456 console.log('expect true: ' + BinaryTree.isFullBinaryTree(newTree)); 457 console.log('lush degree: ' + test.lushDegree()); 458 459 test.printPath_maxDepthS1(); 460 console.log(test.descNum()); 461 }();