javascript實現數據結構: 樹和二叉樹,二叉樹的遍歷和基本操做

樹型結構是一類很是重要的非線性結構。直觀地,樹型結構是以分支關係定義的層次結構。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 }();
View Code
相關文章
相關標籤/搜索