线索二叉树的基本概念

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

在有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. Python MySQLdb连接报2003错误原因

    经测试,本地连接使用:localhost会报2003错误. 解决办法: 使用:127.0.0.1代替:localhost.

  2. Linux System V Semaphore semget多进程同时创建缺陷解决方法

    System V Semaphore的创建过程缺陷是创建与赋初值由两个函数完成,这会导致两个进程同时创建的话会出现竞争和不一致状态,即使是使用了IPC-EXCL标记. 示例: oflag = IPC- ...

  3. mysql新建用户并授权

      参考:https://www.cnblogs.com/zhangdiIT/p/8116776.html

  4. JSON.parse()和JSON.stringify()的使用

    parse用于从一个字符串中解析出json对象,如 var str = '{"name":"huangxiaojian","age":&qu ...

  5. ADO.net开放式并发

    https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/optimistic-concurrency 在多用户环境中,有两种用于更新 ...

  6. Web挖掘

    Web挖掘 Web挖掘的目标是从Web的超链接.网页内容和使用日志中探寻有用的信息.依据Web挖掘任务,可以划分为三种主要类型:Web结构挖掘.Web内容挖掘和Web使用挖掘.Web结构挖掘简单的说就 ...

  7. ZSTU4270 同源数 2017-03-22 14:34 82人阅读 评论(0) 收藏

    4270: 同源数 Time Limit: 3 Sec  Memory Limit: 128 MB Submit: 1284  Solved: 224 Description 如果x和y的质因子集合完 ...

  8. #ifdef __cplusplus extern "C" { #endif 的解释

    好多程序中都会遇到下列代码段: #ifdef __cplusplus extern "C" { #endif /****************** C语法代码段 ******** ...

  9. 前端与HTTP

    本文整理在,我的github 上.欢迎Star. 各版本的http 发展 在HTTP建立之初,主要是为了传输超文本标记语言(HTML)文档.随着时代的发展,也进行了若干次演进.下图是各个版本发布的时间 ...

  10. 基于Struts2开发学生信息管理系统 源码

    开发环境:    Windows操作系统开发工具: Eclipse+Jdk+Tomcat+MYSQL数据库 运行效果图: 联系博主-Q:782827013