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

遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作。如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息?

设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用。则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息。

对结点的指针域做如下规定:

1.若结点有左子树,则其leftChild域指示其左孩子,否则令leftChild域指示其前驱。

2.若结点有右子树,则其rightChild域指示其右孩子,否则令其rightChild域指示其后继。

为了避免混淆,尚需改变结点结构,增加两个标志域(leftTag, rightTag),其中:

      -- 0 leftChild域指示结点的左孩子
 leftTag --
       -- 1 rightChild域指示结点的前驱

      -- 0 rightChild域指示结点的右孩子
 rightTag --
       -- 1 rightChild域指示结点的后继

以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索

加上线索的二叉树称之为线索二叉树(Threaded Binary Tree)

对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化

说明:画线索二叉树时,实线表示指针,指向其左、右孩子;虚线表示线索,指向其直接前驱或直接后继。

在线索树上进行遍历,只要先找到序列中的第一个结点,然后就可以依次找结点的直接后继结点直到后继为空为止。

如何在线索树中找结点的直接后继?

以图(d) ,(e)所示的中序线索树为例:

◆ 树中所有叶子结点的右链都是线索。右链直接指示了结点的直接后继,如结点G的直接后继是结点E。

◆ 树中所有非叶子结点的右链都是指针。根据中序遍历的规律,非叶子结点的直接后继是遍历其右子树时访问的第一个结点,即右子树中最左下的(叶子)结点。如结点C的直接后继:沿右指针找到右子树的根结点F,然后沿左链往下直到Ltag=1的结点即为C的直接后继结点H。

如何在线索树中找结点的直接前驱?

若结点的Ltag=1,则左链是线索,指示其直接前驱;否则,遍历左子树时访问的最后一个结点(即沿左子树中最右往下的结点) 为其直接前驱结点。

对于后序遍历的线索树中找结点的直接后继比较复杂,可分以下三种情况:

◆ 若结点是二叉树的根结点:其直接后继为空;

◆ 若结点是其父结点的左孩子或右孩子且其父结点没有右子树:直接后继为其父结点;

◆ 若结点是其父结点的左孩子且其父结点有右子树:直接后继是对其父结点的右子树按后序遍历的第一个结点。

线索化二叉树

二叉树的线索化指的是依照某种遍历次序使二叉树成为线索二叉树的过程。

线索化的过程就是在遍历过程中修改空指针使其指向直接前驱或直接后继的过程。

仿照线性表的存储结构,在二叉树的线索链表上也添加一个头结点head,头结点的指针域的安排是:

◆ Lchild域:指向二叉树的根结点;

◆ Rchild域:指向中序遍历时的最后一个结点;

◆ 二叉树中序序列中的第一个结点Lchild指针域和最后一个结点Rchild指针域均指向头结点head。

如同为二叉树建立了一个双向线索链表,对一棵线索二叉树既可从头结点也可从最后一个结点开始按寻找直接后继进行遍历。显然,这种遍历不需要堆栈。

线索二叉树的遍历

在线索二叉树中,由于有线索存在,在某些情况下可以方便地找到指定结点在某种遍历序列中的直接前驱或直接后继。此外,在线索二叉树上进行某种遍历比在一般的二叉树上进行这种遍历要容易得多,不需要设置堆栈,且算法十分简洁。

线索二叉树的相关代码实现:

  1 var LINK = 0;
  2 var THREAD = 1;
  3
  4 function BinaryThreadTree_inOrder(data, leftChild, rightChild) {
  5     this.data = data;
  6     this.leftChild = leftChild || null;
  7     this.rightChild = rightChild || null;
  8     // 左右标记
  9     this.leftTag = this.rightTag = undefined;
 10 }
 11 BinaryThreadTree_inOrder.prototype = {
 12     constructor: BinaryThreadTree_inOrder,
 13     // 中序线索二叉树的遍历
 14     inOrderTraverse_thread: function (visit) {
 15         var p = this.leftChild;
 16
 17         while (p != this) {
 18             while (p.leftTag === LINK) p = p.leftChild;
 19
 20             if (visit(p.data) === false) return;
 21
 22             while (p.rightTag == THREAD && p.rightChild != this) {
 23                 p = p.rightChild;
 24                 visit(p.data);
 25             }
 26             p = p.rightChild;
 27         }
 28     },
 29     // 中序线索化
 30     inOrderThreading: function () {
 31         return inOrderThreading(this);
 32     },
 33     // 在当前结点插入子树x,p代表当前结点
 34     insertSubTree: function (xTree) {
 35         var s, q;
 36         // x作为p的左子树
 37         if (this.leftTag === THREAD) {
 38             s = this.leftChild; // s为p的前驱
 39             this.leftTag = LINK;
 40             this.leftChild = xTree;
 41             q = xTree;
 42
 43             while (q.leftChild && q.leftTag === LINK) q = q.leftChild;
 44             // 找到子树中的最左结点,并修改其前驱指向s
 45             q.leftChild = s;
 46             xTree.rightTag = THREAD;
 47             // x的后继指向p
 48             xTree.rightChild = this;
 49         }
 50         // x作为p的右子树
 51         else if (this.rightTag === THREAD) {
 52             // s为p的后继
 53             s = this.rightChild;
 54             this.rightTag = LINK;
 55             this.rightChild = xTree;
 56             q = xTree;
 57
 58             while (q.leftChild && q.leftTag === LINK) q = q.leftChild;
 59             // 找到子树中的最左结点,并修改其前驱指向p
 60             q.leftChild = this;
 61             xTree.rightTag = THREAD;
 62             // x的后继指向p的后继
 63             xTree.rightChild = s;
 64         }
 65         // x作为p的左子树,p的左子树作为x的右子树
 66         else {
 67             s = this.leftChild;
 68             var t = s;
 69
 70             while (t.leftChild && t.leftTag === LINK) t = t.leftChild;
 71             // 找到p的左子树的最左结点t和前驱u
 72             var u = t.leftChild;
 73             this.leftChild = xTree;
 74             xTree.rightTag = LINK;
 75             // x作为p的左子树,p的左子树作为x的右子树
 76             xTree.rightChild = s;
 77             t.leftChild = xTree;
 78             q = xTree;
 79
 80             while (q.leftChild && q.leftTag === LINK) q = q.leftChild;
 81             // 找到子树中的最左结点,并修改其前驱指向u
 82             q.leftChild = u;
 83         }
 84     }
 85 };
 86
 87 // 二叉树中序线索化
 88 function inOrderThreading(tree) {
 89     var threadTree = new BinaryThreadTree();
 90     threadTree.leftTag = LINK;
 91     threadTree.rightTag = THREAD;
 92     // 右指针回指
 93     threadTree.rightChild = threadTree;
 94
 95     var pre;
 96     // 若二叉树为空,左指针回指
 97     if (!tree) threadTree.leftChild = threadTree;
 98     else {
 99         threadTree.leftChild = tree;
100         pre = threadTree;
101         inThreading(tree);  // 中序遍历进行中序线索化
102         // 最后一个结点线索化
103         pre.rightChild = threadTree;
104         pre.rightTag = THREAD;
105         threadTree.rightChild = pre;
106     }
107
108     return threadTree;
109
110     function inThreading(p) {
111         if (!p) return;
112
113         inThreading(p.leftChild);   // 左子树线索化
114         // 前驱线索
115         if (!p.leftChild) {
116             p.leftTag = THREAD;
117             p.leftChild = pre;
118         }
119         // 后继线索
120         if (!pre.rightChild) {
121             pre.rightTag = THREAD;
122             pre.rightChild = p;
123         }
124         pre = p;
125         inThreading(p.rightChild);  // 右子树线索化
126     }
127 }

javascript实现数据结构:线索二叉树,布布扣,bubuko.com

时间: 2024-10-17 10:29:41

javascript实现数据结构:线索二叉树的相关文章

(原)数据结构——线索二叉树

原文地址:http://www.cnblogs.com/Security-Darren/p/4716082.html 转载务必注明出处! 线索二叉树的思想来源于二叉树的存储结构中,存在一些空的指针域,因此是否能够将这些空间利用起来,存储一些关于节点间先后顺序的信息,由此产生了线索二叉树.线索二叉树中,线索反映前驱.后继的关系,而指针则体现左右子树. 以二叉链表为例,线索二叉树存储结构上的特点是添加标识符,表明左右指针域究竟存的是指向前驱和后继的线索,还是指向左右子树的指针: 线索二叉树的优势是一

数据结构 - 线索二叉树

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

数据结构 线索二叉树 原理及实现

通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数.准确的说,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个.如下图所示. 因此,提出了一种方法,利用原来的空链域存放指针,指向树中其他结点.这种指针称为线索. 记ptr指向二叉链表中的一个结点,以下是建立线索的规则: (1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点.这个结点称为ptr的中序前驱: (2)如果ptr->rchild为空,则存放指向中序遍历

重拾算法(2)——线索二叉树

重拾算法(2)——线索二叉树 上一篇我们实现了二叉树的递归和非递归遍历,并为其复用精心设计了遍历方法Traverse(TraverseOrder order, NodeWorker<T> worker);今天就在此基础上实现线索二叉树. 什么是线索二叉树 二叉树中容易找到结点的左右孩子信息,但该结点在某一序列中的直接前驱和直接后继只能在某种遍历过程中动态获得. 先依遍历规则把每个结点某一序列中对应的前驱和后继线索预存起来,这叫做"线索化". 意义:从任一结点出发都能快速找到

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

树型结构是一类非常重要的非线性结构.直观地,树型结构是以分支关系定义的层次结构. 树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构:在数据库系统中,可用树来组织信息:在分析算法的行为时,可用树来描述其执行过程等等. 下面讲解的内容完整代码在这:https://github.com/LukeLin/data-structure-with-js/blob/master/Binary%20tree/BinaryTree.js 首先看看树的一些概念: 1.树(Tree)是n

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

【算法与数据结构】二叉树 中序线索

中序线索二叉树 /************************************************************************ 线索二叉树 二叉树的节点有五部分构造 ----------------------------------------- | lChild | lTag | value | rTag | rChild | ----------------------------------------- lChild = (lTag == 0 ? 左

【数据结构】中序遍历线索二叉树

昨天写了个二叉树遍历,自以为对二叉树很了解了.自大的认为线索二叉树不过是加了点线索而已,不足挂齿.可是当真的自己编程序写的时候才发现完全不是那么容易.在有线索的情况下,如何判别Link类型的下一节点,如何不用栈跳过已访问节点搞得脑子晕晕的. 折腾一个晚上,才根据书上把线索二叉树的建立.中序遍历给写出来.要回去继续好好的理清关系. #include <stdio.h> #include <stdlib.h> typedef enum PointerTag{Link, Thread};

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

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