javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作

树型结构是一类非常重要的非线性结构。直观地,树型结构是以分支关系定义的层次结构。

树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构;在数据库系统中,可用树来组织信息;在分析算法的行为时,可用树来描述其执行过程等等。

下面讲解的内容完整代码在这:https://github.com/LukeLin/data-structure-with-js/blob/master/Binary%20tree/BinaryTree.js

首先看看树的一些概念:

1.树(Tree)是n(n>=0)个结点的有限集。在任意一棵非空树中:

  (1)有且仅有一个特定的称为根(Root)的结点;

  (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,T3,...Tm,其中每一个集合本身又是一棵树,并且称为根的子树(Subtree)。

例如,(a)是只有一个根结点的树;(b)是有13个结点的树,其中A是根,其余结点分成3个互不相交的子集:T1={B,E,F,K,L},t2={D,H,I,J,M};T1,T2和T3都是根A的子树,且本身也是一棵树。

2.树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的(Degree)。例如,(b)中A的度为3,C的度为1,F的度为0.度为0的结点称为叶子(Leaf)或者终端结点。度不为0的结点称为非终端结点或分支结点。树的度是树内各结点的度的最大值。(b)的树的度为3.结点的子树的根称为该结点的孩子(Child)。相应的,该结点称为孩子的双亲(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根到该结点所经分支上的所有结点。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。

3.结点的层次(Level)从根开始定义起,根为第一层,跟的孩子为第二层。若某结点在第l层,则其子树的根就在第l+1层。其双亲在同一层的结点互为堂兄弟。例如,结点G与E,F,H,I,J互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度。(b)的树的深度为4。

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;

  ⑴ 访问p所指向的结点;

  ⑵ q=p.rightChild ,若q不为空,则q进栈;

  ⑶ p=p.leftChild ,若p不为空,转(1),否则转(4);

  ⑷ 退栈到p ,转(1),直到栈空为止。

代码实现:

    // 链式存储的非递归先序遍历

    // 方法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域决定做何动作

    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 }();

javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作,布布扣,bubuko.com

时间: 2024-08-03 07:13:22

javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作的相关文章

数据结构 - 树和森林表示与遍历

双亲表示法(顺序存储结构) 用一组连续的存储空间来存储树的结点,同时在每个结点中附加一个指示器(整数域) ,用以指示双亲结点的位置(下标值) .数组元素及数组的类型定义如下: #define MAX_SIZE 100 typedef struct PTNode { ElemType data ; int parent ; }PTNode ; typedef struct { PTNode Nodes[MAX_SIZE] ; int root; /* 根结点位置 */ int num ; /* 结

javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径. ② 路径长度:结点路径上的分支数目称为路径长度. ③ 树的路径长度:从树根到每一个结点的路径长度之和. 以下图为例: A到F :结点路径 AEF : 路径长度(即边的数目) 2 : 树的路径长度:3*1+5*2+2*3=19: ④ 结点的带权路径长度:从该结点的到树的根结

javascript实现数据结构:线索二叉树

遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用.则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息. 对结点的指针域做如下规定: 1.若结点有左子树,则其leftChild域指示其左孩子,否则令leftC

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷

6. 蛤蟆的数据结构进阶六之二叉树排序树

6. 蛤蟆的数据结构进阶六之二叉树排序树 本篇名言:"有些人因为贪婪,想得更多的东西,却把现在所有的也失掉了. -- 伊索" 关于理论知识已经在上篇中进行描述,这篇我们主要来看下如何是实现二叉排序树. 欢迎转载,转载请标明出处: 1.  二叉排序树的定义 二叉排序树(BinarySort Tree)又称二叉查找(搜索)树(Binary Search Tree).其定义为:二叉排序树或者是空树,或者是满足如下性质的二叉树: ①若它的左子树非空,则左子树上所有结点的值均小于根结点的值: ②

数据结构---树、二叉树、森林

1.基本术语: 度:有两种度"结点的度"与"树的度".结点的度指的是一个结点子树的个数:树的度是指树中结点度的最大值. 叶子结点:指的是没有子树的结点. 层:树是有层次的,一般根结点为第0层.规定根结点到某结点的路径长度为该结点的层数. 深度:树中结点的最大层数 兄弟:同一双亲的结点,互为兄弟 堂兄弟:双亲在同一层次的结点,互为堂兄弟 祖先:从根结点到该结点的路径上的所有结点都是该结点的祖先. 子孙:以某一结点为根的子树上的所有结点都是该结点的子孙 森林:n棵互不相

浅谈数据结构-树和二叉树之间关系

树都可用二叉链表作为存储结构,对比各自的结点结构可以看出,以二叉链表作为媒介可以导出树和二叉树之间的一个对应关系. ◆ 从物理结构来看,树和二叉树的二叉链表是相同的,只是对指针的逻辑解释不同而已. ◆ 从树的二叉链表表示的定义可知,任何一棵和树对应的二叉树,其右子树一定为空. 1 树转换成二叉树 对于一般的树,可以方便地转换成一棵唯一的二叉树与之对应.将树转换成二叉树在"孩子兄弟表示法"中已给出,其详细步骤是: ⑴ 加虚线.在所有兄弟结点之间加线. ⑵ 去连线.只保留大孩子(除最左的第

数据结构--树(上)-- 二叉树的遍历

二叉树的遍历 主要是以二叉树的链式存储来讲. 链表存储的结构:(下面会用到) typedef struct TreeNode *BinTree; typedef BinTree Position; struct TreeNode { ElementType Data; BinTree Left; BinTree Right; } 二叉树的递归遍历 1.前序遍历 访问顺序是根节点,左子树,右子树.这样的过程实际上就是一种递归.用递归实现 void PreOrderTraversal(BinTree

C#数据结构—树和二叉树

线性结构中的数据元素是一对一的关系,树形结构是一对多的非线性结构,非常类似于自然界中的树,数据元素之间既有分支关系,又有层次关系.树形结构在现实世界中广泛存在,如家族的家谱.一个单位的行政机构组织等都可以用树形结构来形象地表示.树形结构在计算机领域中也有着非常广泛的应用,如 Windows 操作系统中对磁盘文件的管理.编译程序中对源程序的语法结构的表示等都采用树形结构.在数据库系统中,树形结构也是数据的重要组织形式之一.树形结构有树和二叉树两种,树的操作实现比较复杂,但树可以转换为二叉树进行处理