「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」。
本文介绍了线索二叉树的概念,以及线索二叉树的Java的实现。如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择。
1 线索二叉树的概述
如果对二叉树的概念不是很了解,可以先看这篇文章:二叉树的入门以及Java实现案例详解。二叉树的概念在此不做赘述。
对于一个有n个结点的二叉链表,每个结点有指向左右孩子的两个引用域,所以一共是2n个引用域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n-(n-1)=n+1个空引用域。这些空间不存储任何事物,白白的浪费着内存的资源。
如下图:
二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的引用,这种方法增加了存储开销,不可取;二是利用二叉树的空链引用。
我们可以考虑利用那些空地址,存放指向结点在某种遍历次序下的前驱和后继结点的地址,空left存放前驱,空right存放后继。我们把这种指向前驱和后继的引用称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree)。
对二叉树以某种遍历方式(如先序、中序、后序或层序)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。 根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
采用中序遍历线索化之后的数据结构如下:
可以看到,它充分利用了空引用域的空间(这等于节省了空间),又保证了创建时的一次遍历就可以终生受用前驱后继的信息(这意味着节省了时间)。所以在实际问题中,如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择。
2 节点设计
线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索引用和孩子引用,在每个结点中设置两个标志ltag和rtag。
当tag和rtag为0时,left和right分别是指向左孩子和右孩子的指针;否则,left是指向结点前驱的线索(pre),right是指向结点的后继线索(suc)。由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。
现将二叉树的结点结构重新定义如下:
left | ltag | data | rtag | right |
---|
其中:ltag=0 时left指向左儿子;ltag=1 时left指向前驱;rtag=0 时right指向右儿子;rtag=1 时right指向后继。
3 中序线索二叉树的构建
现提供中序线索二叉树的构建的完整Java代码,先序、后续线索二叉树的构建和中序的构建基本一致,都是在先、中、后序遍历的方法的基础上改进而来的,如果对关于二叉树的4种遍历方式不了解的,可以看这篇文章:二叉树的4种遍历方式详解以及Java代码的完整演示。
1 | java复制代码public class ThreadedBinaryTree<E> { |
3.1 测试
1 | java复制代码public class ThreadTreeTest { |
如果有什么不懂或者需要交流,可以留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!
本文转载自: 掘金