数据结构之二叉树(三):线索化二叉树
基本介绍:
线索化二叉树(Threaded BinaryTree):
有n个节点的二叉链表中含有n+1【公式2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针与, 存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针成为“线索")
这种加上了线索的二叉链表称为线索链表,相应的二叉树成为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树,中序线索二叉树和后序线索二叉树三种。
一个节点的前一个节点,称为前驱节点
一个节点的后一个节点,成为后继节点
官方的解释太过板正,请看下图:
通过上图我们可发现,这是一颗完全二叉树,但是,叶子节点8,10,14,和6,它们的左右节点不存在(6只有左节点),也就是说,它们的节点上都存在着指针没有被利用。
所谓线索化二叉树,便是将这些未被利用起来的指针使用起来,让各个节点可以指向自己的前后节点.
而为什么又会分为前序前序线索二叉树,中序线索二叉树和后序线索二叉树三种情况呢?这是因为我们在进行遍历线索化二叉树时,由于遍历的方式不同而导致的线索指针的指向不同。
说到这里如果还是不太明白,请看如下的图文解释:
线索化之前的二叉树:
它的中序遍历结果为 [8,3,10,1,14,6];
通过中序遍历的结果去线索化二叉树得到如下:
由上图可知,
8节点中序遍历时排在首位,所以它的前驱节点并不存在,后继节点为3,于是它的空闲指针便被利用起来,使它的后继节点指向了3。
10节点中序遍历是排在第三位,它的左右节点指针皆为空闲,所以我们可将它的左节点指向它的前驱节点3,右节点指向它的后继节点1.
同样,14节点的左右指针同上。
问题分析:
由上面的图文想必大家都对于线索化二叉树已经有了个明确的认知。
但是,采用这种方式构建线索化二叉树在代码实现的时候势必会有一些难点。·
当线索化二叉树后,Node节点的left和right会有如下的情况:
left指向的是左子树,也可能是指向的是前驱节点。比如图中1节点的左子树指向的是它的左子树,而10节点指向的则是它的前驱节点。
right同理。
所以,这就为我们代码实现提供了一定的难度。
参考代码如下:
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
BinaryTree threadBinaryTree=new BinaryTree();
HeroNode root=new HeroNode("孙悟空");
HeroNode node1=new HeroNode("猪八戒");
HeroNode node2=new HeroNode("唐三藏");
HeroNode node3=new HeroNode("沙和尚");
HeroNode node4=new HeroNode("牛魔王");
HeroNode node5=new HeroNode("红孩儿");
HeroNode node6=new HeroNode("白骨精");
threadBinaryTree.setRoot(root);
root.setLeftNode(node1);
root.setRightNode(node2);
node1.setLeftNode(node3);
node1.setRightNode(node4);
node2.setLeftNode(node5);
node2.setRightNode(node6);
threadBinaryTree.threadNodes();
threadBinaryTree.threadshow();
}
}
class BinaryTree{
HeroNode root;
//指向前驱节点的指针
//在递归进行线索化时,pre总是保留前一个节点
HeroNode pre=null;
public void setRoot(HeroNode root) {
this.root=root;
}
//重载threadNodes方法,使node首先为root
public void threadNodes() {
threadNodes(root);
}
//线索化二叉树的方法(中序遍历方式)
public void threadNodes(HeroNode node) {
//先判断node是否为空,为空则不再线索化
if(node==null) {
return;
}
//先线索化左子树
threadNodes(node.getLeftNode());
//线索化当前节点
//先处理前驱节点
if(node.leftNode==null) {
//让当前节点额左指针指向前驱节点
node.setLeftNode(pre);
//设置当前节点的type为1
node.setLeftType(1);
}
//再处理后继节点
if(pre!=null&&pre.getRightNode()==null) {
//让前驱节点的右指针指向当前节点
pre.setRightNode(node);
//修改前驱节点的type为1
pre.setRightType(1);
}
//每处理一个节点后,让当前节点时下一个节点的前驱节点
pre=node;
//再线索化右子树
threadNodes(node.getRightNode());
}
//循环遍历线索化二叉树(中序遍历顺序)
public void threadshow() {
//定义一个变量,存储当前遍历的节点,从root开始
HeroNode node=root;
while(node!=null) {
while(node.getLeftType()==0) {
node=node.getLeftNode();
}
System.out.println(node.value);
while(node.getRightType()==1) {
node=node.getRightNode();
System.out.println(node.value);
}
node=node.getRightNode();
}
}
public void frontShow() {
if(this.root!=null) {
this.root.frontShow();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void middleShow() {
if(this.root!=null) {
this.root.middleShow();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void endShow() {
if(this.root!=null) {
this.root.endShow();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void deleteNode(String value) {
if(this.root!=null) {
if(this.root.value.equals(value)) {
this.root=null;
}else {
root.deleteNode(value);
}
}else {
System.out.println("空树,无法删除");
}
}
}
class HeroNode{
HeroNode leftNode;
HeroNode rightNode;
String value;
//两个参数解决左右子树和前后节点的的混乱问题
//如果leftType或rightType==0,表示指向的是左右子树,如果等于1,表示指向的是前后节点
private int leftType;
private int rightType;
public HeroNode(String value) {
this.value=value;
}
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 getLeftNode() {
return leftNode;
}
public void setLeftNode(HeroNode leftNode) {
this.leftNode=leftNode;
}
public HeroNode getRightNode() {
return rightNode;
}
public void setRightNode(HeroNode rightNode) {
this.rightNode=rightNode;
}
@Override
public String toString() {
return "HeroNode [leftNode=" + leftNode + ", rightNode=" + rightNode + ", value=" + value + "]";
}
/**
* 三种遍历方法
*/
//前序遍历:
public void frontShow() {
System.out.println(this.value);
if(this.getLeftNode()!=null) {
this.getLeftNode().frontShow();
}
if(this.getRightNode()!=null) {
this.getRightNode().frontShow();
}
}
//中序遍历
public void middleShow() {
if(this.getLeftNode()!=null) {
this.getLeftNode().middleShow();
}
System.out.println(this.value);
if(this.getRightNode()!=null) {
this.getRightNode().middleShow();
}
}
//后序遍历
public void endShow() {
if(this.getLeftNode()!=null) {
this.getLeftNode().endShow();
}
if(this.getRightNode()!=null) {
this.getRightNode().endShow();
}
System.out.println(this.value);
}
public void deleteNode(String value) {
if(this.leftNode!=null&&this.leftNode.value.equals(value)) {
this.leftNode=null;
return;
}
if(this.rightNode!=null&&this.rightNode.value.equals(value)) {
this.rightNode=null;
return;
}
if(this.leftNode!=null) {
this.leftNode.deleteNode(value);
}
if(this.rightNode!=null) {
this.rightNode.deleteNode(value);
}
}
}
输出结果: