线索二叉树的基本概念

我们按某种方式对二叉树进行遍历,将二叉树中所有节点排序为一个线性序列,在该序列中,除第一个结点外每个结点有且仅有一个直接前驱结点;除最后一个结点外每一个结点有且仅有一个直接后继结点。

在有N个节点的二叉树中需要利用N+1个空指针添加线索,这是因为在N个节点的二叉树中,每个节点有2个指针,所以一共有2N个指针,除了根节点以外,每一个节点都有一个指针从它的父节点指向它,所以一共使用了N-1个指针,所以剩下2N-(N-1)也就是N+1个空指针;

我们利用这些空指针域来存放指向该节点的直接前驱或是直接后继的指针,则可由此信息直接找到在该遍历次序下的前驱结点或后继结点,从而比递归遍历提高了遍历速度,节省了建立系统栈所使用的存储空间;

这些被重新利用起来的空指针就被称为线索(Thread),加上了这些线索的二叉树就是线索二叉树

根据线索性质的不同,线索二叉树可以分为前序线索二叉树,中序线索二叉树,后序线索二叉树。

记node指向二叉链表中的一个结点,以下是建立线索的规则:

(1)如果node的左指针域为空,则存放指向某种遍历序列中该结点的前驱结点,这个结点称为node的前驱;

(2)如果node的右指针域为空,则存放指向中序遍历序列中该结点的后继结点。这个结点称为node的后继;

代码实现

修改二叉树节点数据结构,节点类:

package 线索二叉树;

public class ClueNode {

	private Object data;
private ClueNode left;
private ClueNode right;
private boolean leftIsThread;
private boolean rightIsThread; public ClueNode(Object data) {
this.data = data;
this.left = null;
this.right = null;
this.leftIsThread = false;
this.rightIsThread = false;
} public ClueNode(Object data, ClueNode left, ClueNode right, boolean leftIsThread, boolean rightIsThread) {
this.data = data;
this.left = left;
this.right = right;
this.leftIsThread = leftIsThread;
this.rightIsThread = rightIsThread;
} public Object getData() {
return this.data;
}
public void setData(Object data ) {
this.data = data;
}
public ClueNode getLeft() {
return left;
} public void setLeft(ClueNode left)
{
this.left = left;
} public boolean isLeftIsThread()
{
return leftIsThread;
} public void setLeftIsThread(boolean leftIsThread)
{
this.leftIsThread = leftIsThread;
} public ClueNode getRight()
{
return right;
} public void setRight(ClueNode right)
{
this.right = right;
} public boolean isRightIsThread()
{
return rightIsThread;
} public void setRightIsThread(boolean rightIsThread)
{
this.rightIsThread = rightIsThread;
} public boolean equals(Object o) {
if(o instanceof ClueNode) {
ClueNode clue = (ClueNode)o;
return (clue.getData() == this.data) ? true : false;
}
else return false;
} }

线索二叉树类:

package 线索二叉树;

public class ClueForkTree {

	private ClueNode preNode;

	//根据数组构建完全二叉树
public static ClueNode createClueForkTree(Object[] array, int index) {
ClueNode node = null;
if(index < array.length) {
node = new ClueNode(array[index]);
ClueNode left = createClueForkTree(array, index * 2 + 1);
ClueNode right = createClueForkTree(array, index * 2 + 2);
node.setLeft(left);
node.setRight(right);
return node;
}
else return null;
} //中序线索化二叉树
public void inThReading(ClueNode node) {
if(node == null) return; inThReading(node.getLeft()); if(node.getLeft() == null) {
node.setLeft(preNode);
node.setLeftIsThread(true);
} if(preNode != null && preNode.getRight() == null) {
preNode.setRight(node);
preNode.setRightIsThread(true);
}
preNode = node; inThReading(node.getRight());
} //中序按后继方式遍历线索二叉树
public void inThreadList(ClueNode node) {
while(node != null && !node.isLeftIsThread()) {
node = node.getLeft();
} while(node != null) {
System.out.print(node.getData() + ","); if(node.isRightIsThread()) {
node = node.getRight();
}
else {
node = node.getRight();
while(node != null && !node.isLeftIsThread()) {
node = node.getLeft();
}
}
}
} //中序按前驱方式遍历线索二叉树
public void inPreThreadList(ClueNode node) {
while(node.getRight() != null && !node.isRightIsThread()) {
node = node.getRight();
}
while(node != null) {
System.out.print(node.getData() + ",");
if(node.isLeftIsThread()) {
node = node.getLeft();
}
else {
node = node.getLeft();
while(node.getRight() != null && !node.isRightIsThread()) {
node = node.getRight();
}
}
}
} //前序线索化二叉树
public void inThFrontReading(ClueNode node) {
if(node == null) return; if(node.getLeft() == null) {
node.setLeft(preNode);
node.setLeftIsThread(true);
} if(preNode != null && preNode.getRight() == null) {
preNode.setRight(node);
preNode.setRightIsThread(true);
} preNode = node;
if(!node.isLeftIsThread()) {
inThFrontReading(node.getLeft());
} if(!node.isRightIsThread()) {
inThFrontReading(node.getRight());
} } //前序按后继方式进行遍历二叉树
public void preThreadList(ClueNode node) {
while(node != null) {
while(!node.isLeftIsThread()) {
System.out.print(node.getData() + ",");
node = node.getLeft();
}
System.out.print(node.getData() + ",");
node = node.getRight(); }
} public static void main(String[] args) {
String[] array = {"A", "B", "C", "D", "E", "F", "G", "H"};
ClueNode root = createClueForkTree(array, 0);
ClueForkTree tree = new ClueForkTree();
tree.inThReading(root);
System.out.println("中序按后继节点遍历线索二叉树结果:");
tree.inThreadList(root); System.out.println("\n中序按前驱节点遍历线索二叉树结果:");
tree.inPreThreadList(root); ClueNode root1 = createClueForkTree(array, 0);
tree.inThFrontReading(root1);
tree.preNode = null;
System.out.println("\n前序按后继节点遍历线索二叉树结果:");
tree.preThreadList(root1); }
}

线索二叉树的理解和实现(Java)的更多相关文章

  1. 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)

    本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...

  2. 【PHP数据结构】完全二叉树、线索二叉树及树的顺序存储结构

    在上篇文章中,我们学习了二叉树的基本链式结构以及建树和遍历相关的操作.今天我们学习的则是一些二叉树相关的概念以及二叉树的一种变形形式. 完全二叉树 什么叫完全二叉树呢?在说到完全二叉树之前,我们先说另 ...

  3. Atitit  深入理解命名空间namespace  java c# php js

    Atitit  深入理解命名空间namespace  java c# php js 1.1. Namespace还是package1 1.2. import同时解决了令人头疼的include1 1.3 ...

  4. 数据结构《9》----Threaded Binary Tree 线索二叉树

    对于任意一棵节点数为 n 的二叉树,NULL 指针的数目为  n+1 , 线索树就是利用这些 "浪费" 了的指针的数据结构. Definition: "A binary ...

  5. 线索二叉树Threaded binary tree

    摘要   按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有结点排序为一个线性序列.在该序列中,除第一个结点外每个结点有且仅有一个直接前驱结点:除最后一个结点外每一个结点有且仅有一个直接后继结点.这 ...

  6. 遍历二叉树 traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化

    遍历二叉树   traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化 1. 二叉树3个基本单元组成:根节点.左子树.右子树 以L.D.R ...

  7. 理解和解决Java并发修改异常ConcurrentModificationException(转载)

    原文地址:https://www.jianshu.com/p/f3f6b12330c1 理解和解决Java并发修改异常ConcurrentModificationException 不知读者在Java ...

  8. 树和二叉树->线索二叉树

    文字描述 从二叉树的遍历可知,遍历二叉树的输出结果可看成一个线性队列,使得每个结点(除第一个和最后一个外)在这个线形队列中有且仅有一个前驱和一个后继.但是当采用二叉链表作为二叉树的存储结构时,只能得到 ...

  9. 图解中序遍历线索化二叉树,中序线索二叉树遍历,C\C++描述

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

随机推荐

  1. Django基础模板案例

    想要用django  访问一个页面 同时传参数过去.在页面中接受参数 案例:附代码 #创建一个项目 项目名字是 yhl_test django-admin startproject yhl_test ...

  2. hack vba password, en useful...

    Unbelivibale, but I found a very simple way that really works! Do the follwoing: 1. Create a new sim ...

  3. re 正则模块

    re模块(* * * * *) 就其本质而言,正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译成一系列 ...

  4. chrome扩展安装图文教程

    众所周知chrome的各类插件,扩展很丰富,也有很多经典的应用.但是谷歌经常被墙,无法访问,想要通过访问谷歌的应用市场来直接安装浏览器扩展会比较让人抓狂,好不容易无数次刷新后打开了页面,找到了想要的应 ...

  5. CSGL

    glShadeModel void glShadeModel(GLenum mode) GL_FLAT/[GL_SMOOTH] 着色技术选择 glClearDepth GL.glClearDepth( ...

  6. [原创]使用OPENCC库进行简繁转换(C++代码)

    最近公司有一款游戏产品,字库存在问题,希望全自动进行简繁同屏自动转换的行为,减少工作量. 所以自己使用了WINDOWS自带的一些转换函数,但发现大量字出现异常,无法转换(测试iconv也发现无法转换) ...

  7. 关于int转char类型引发的一些思考

    signed char unsigned char

  8. pom.xml的继承、聚合与依赖

    原文地址:https://my.oschina.net/zh119893/blog/232896 6.1     简介 pom.xml文件是Maven进行工作的主要配置文件.在这个文件中我们可以配置M ...

  9. UVa 10537 The Toll! Revisited (最短路)

    题意:给定一个图,你要从 s 到达 t,当经过大写字母时,要交 ceil(x /20)的税,如果经过小写字母,那么交 1的税,问你到达 t 后还剩下 c 的,那么最少要带多少,并输出一个解,如果多个解 ...

  10. Linux 基础教程 45-read命令

    基本用法     read命令主要用于从标准输入读取内容或从文件中读取内容,并把信息保存到变量中.其常用用法如下所示: read [选项] [文件] 选项 解释 -a array 将内容读取到数值中, ...