目录
定义一个PostTreadBinaryTree 来表示节点信息:
编写createBinaryTree方法来实现二叉树的创建:
写在之前:在学习线索化二叉树之前,要对二叉树的前中后序遍历有一定的了解,如果还不会的小伙伴可以看看我的这篇博客:二叉树-------前,中,后序遍历 + 前,中,后序查找+删除节点 (java详解)-优快云博客
由于网上以及书上对线索化二叉树的前序和后序及其遍历介绍的较少,这里统一整理了一下,希望对各位有所帮助。欢迎点赞加关注支持博主~
-------------------------------------------------------------------------------------------------------------------------------
线索化二叉树的基本介绍:
- n个结点的二叉链表中含有n+1 【公式 2n-(n-1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")
- 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
- 一个结点的前一个结点,称为前驱结点
- 一个结点的后一个结点,称为后继结点
举个栗子:
将一颗普通的二叉树如下图所示(它是一个含有6个节点的二叉树,按照上述公式计算其空指针域为7【n+1】):
中序线索化后,得到如图所示的二叉树(中序线索化后):
这实际上是一个将二叉树的指针域充分利用的过程(即充分利用各个节点的左右指针),那么,上述的过程究竟是如何实现的呢?看到这里,你想必还是一脸懵圈,别急,然我们再举个栗子(以中序线索化为例):
我们先将上述数字转化为一个数组,当然这个数组不是随便写的,是通过中序遍历得到的,这个数组是:8,3,10,1,14,6 ,接着我们只需要结合原图,将左右指针为空的节点进行连线,具体如下:
说明:数字8左右指针都为空,理应联两条线,但根据其中序遍历的结果,它的左边为空,所以上述图中数字8只连了一根线,对应到3节点。同理数字10连两根线对应3,1;数字14连2根线,对应1,6;6连一根线,对应空(null),实际在图中表示为不连线---》连线的根数是根据节点的左右指针个数决定的,如数字8,节点左右指针为空,连两条线(对应左右指针的方向),而数字6右指针为空,所以连一条线
实际线索化后的二叉树(通常null省略罢了 ):
通过上面的一些描述,想必你对二叉树的线索化有了一定理解,那么我们接着开始进入正题:
二叉树的中序线索化:
说明:当线索化二叉树后,Node节点的属性left和right有如下情况:
1).left指向的是左子树,也可能是指向前驱节点,比如①节点left指向左子树,二⑩节点的left指向的就是前驱节点
2).right指向的是右子树,也可能是指向后继节点,比如①节点right指向左=右子树,二⑩节点的right指向的就是后继节点
同时,为了方便表示线索后的二叉树,我们这样定义: 1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点 2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点
创建HeroNode类,表示节点信息:
//先创建HeroNode 结点
class HeroNode {
private int no;
private String name;
private HeroNode left; //默认null
private HeroNode right; //默认null
//说明
//1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点
//2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点
private int leftType;
private int rightType;
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + "]";
}
}*/
通过上面的中序线索化构造的栗子,我们可以按照先
(一)先线索化左子树---》(二)线索化当前结点---》(三)在线索化右子树
编写中序线索化方法代码:
public void threadedNodes(HeroNode node) {
//如果node==null, 不能线索化
if (node == null) {
return;
}
//(一)先线索化左子树
threadedNodes(node.getLeft());
//(二)线索化当前结点
//处理当前结点的前驱结点
//以8结点来理解
//8结点的.left = null , 8结点的.leftType = 1
if (node.getLeft() == null) {
//让当前结点的左指针指向前驱结点
node.setLeft(pre);
//修改当前结点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
//处理后继结点
if (pre != null && pre.getRight() == null) {
//让前驱结点的右指针指向当前结点
pre.setRight(node);
//修改前驱结点的右指针类型
pre.setRightType(1);
}
//!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
pre = node;
//(三)在线索化右子树
threadedNodes(node.getRight());
}
中序线索化遍历代码:
/遍历线索化二叉树的方法
public void threadedList() {
//定义一个变量,存储当前遍历的结点,从root开始
HeroNode node = root;
while (node != null) {
//循环的找到leftType == 1的结点,第一个找到就是8结点
//后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
//处理后的有效结点
while