线索二叉树的基本概念

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

在有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. xshell上传下载文件(Windows、Linux)

    经常有这样的需求,我们在Windows下载的软件包,如何上传到远程Linux主机上?还有如何从Linux主机下载软件包到Windows下:之前我的做法现在看来好笨好繁琐,不过也达到了目的,笨人有本方法 ...

  2. 博客搬家到blog.wu8685.com

    博客园算是我最开始来的地方了吧,当时还在学校,为了找工作会看一些理论方面的东西,所以写的都是偏理论的心得. 后来参加了工作,开始忙起来,也就没有时间来更新了.其实忙都是借口,这点还是需要反思的. 大概 ...

  3. 超赞!UX写手必备技能

    以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 今天,小编非常荣幸能与大家一起分享一些优秀UX 写手必备的成功技能: 1.开篇抓住用户的心 MBE曾 ...

  4. linq join一些忘记的操作

  5. selenium设置代理,基于chrome浏览器

    工作中遇到需要对项目中使用的selenium设置代理,跟大家分享一下. 1.下载chromeDriver:http://chromedriver.storage.googleapis.com/inde ...

  6. solr特点五: MoreLikeThis(查找相似页面)

    在 Google 上尝试一个查询,您会注意到每一个结果都包含一个 “相似页面” 链接,单击该链接,就会发布另一个搜索请求,查找出与起初结果类似的文档.Solr 使用MoreLikeThisCompon ...

  7. 解决IIS中Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'.

  8. Programmatically Disable Event Firing on List Item Update in SharePoint 2010

    1. Microsoft.SharePoint.dll Create EventFiring.cs 1.Right-click on the project, select Add and click ...

  9. 管道/FIFO注意事项

    管道 1. 其本质是一个伪文件(实为内核缓冲区) 2. 由两个文件描述符引用,一个表示读端,一个表示写端. 3. 规定数据从管道的写端流入管道,从读端流出. 管道的原理: 管道实为内核使用环形队列机制 ...

  10. kolla-ansible安装openstack(Ocata)

    基本功能部署 基础环境 角色 操作系统 硬件配置 Depoly CentOS 7 Server 磁盘:40GB 内存:8GB 网卡:ens3(内网) ens4(外网) Sched CentOS 7 S ...