一、问题引入
将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. n+1=7
问题分析:
1)
当我们对上面的二叉树进行中序遍历时,数列为
{8, 3, 10, 1, 6, 14 }
2)
但是
6, 8, 10, 14
这几个节点的 左右指针,并没有完全的利用上
.
3)
如果我们希望充分的利用 各个节点的左右指针, 让各个节点可以指向自己的前后节点
,
怎么办
?
4)
解决方案
-
线索二叉树
二、线索二叉树基本介绍
1)
n
个结点的二叉链表中含有
n+1 【
公式
2n-(n-1)=n+1】
个
空指针域
。利用二叉链表中的空指针域,存放指向
该
结点
在
某种遍历次序
下的
前驱
和
后继
结点的指针(这种附加的指针称为
"
线索
"
)
2)
这种加上了线索的二叉链表称为
线索链表
,相应的二叉树称为
线索二叉树
(Threaded
BinaryTree
)
。根据线索性质的不同,线索二叉树可分为
前序线索二叉树、中序线索二叉树
和
后序线索二叉树
三种
3)
一个结点的前一个结点,称为
前驱
结点
4)
一个结点的后一个结点,称为
后继
结点
三、线索化二叉树的案例
思路分析: 中序遍历的结果:{8, 3, 10, 1, 14, 6}
说明: 当线索化二叉树后,Node节点的 属性 left 和 right ,有如下情况:
1)
left
指向的是左子树,也可能是指向的前驱节点
.
比如 ① 节点
left
指向的左子树
,
而 ⑩ 节点的
left
指向的就是前驱节点
.
2)
right
指向的是右子树,也可能是指向后继节点,比如 ① 节点
right
指向的是右子树,而⑩ 节点的
right
指向的是后继节点
.
四、遍历线索二叉树案例
说明:对前面的中序线索化的二叉树, 进行遍历
分析:因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。 遍历的次序应当和中序遍历保持一致。
五、代码演示(以中序为例)
1.定义节点(新增两个属性,标识left、right 的含义)
class threadTreeNode {
private int no;
private String name;
private threadTreeNode left;
private threadTreeNode right;
private Boolean leftType=false;//true-前驱 false-左子节点
private Boolean rightType=false;//true-后继 false-右子节点
2.定义线索二叉树
class threadBinaryTree{
private threadTreeNode pre=null;//前驱
private threadTreeNode root;//根节点
public threadBinaryTree(threadTreeNode root) {
this.root = root;
}
public threadTreeNode getRoot() {
return root;
}
public void setRoot(threadTreeNode root) {
this.root = root;
}
//1. 中序线索化
public void Threaded(threadTreeNode node){
if(node==null) return;
Threaded(node.getLeft());
//如果一个节点左子树为空,就让它指向该节点的前驱
if (pre != null && node.getLeft() == null) {
node.setLeft(pre);
node.setLeftType(true);
}
//如果某个点的前驱的右子树为空,且该前驱不为空,那就让前驱的右子树指向某点
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(true);
}
pre = node;//让pre总指向当前节点的前一个节点
Threaded(node.getRight());
}
//2. 中序遍历线索二叉树
public void infixOrderThreadedBinaryTree(){
threadTreeNode node=root;
while(node!=null){
//向左找到有前驱的节点
while (node.getLeftType()==false && node.getLeft()!=null){
node=node.getLeft();
}
//输出节点
System.out.println(node);
//找到有后继的节点,遍历到它的后继并输出
while (node.getRightType()==true){
node=node.getRight();
System.out.println(node);
}
//如果一个点没前驱也没后继,就向它的右子节点移动
node=node.getRight();
}
}
}
3.测试
3.1检查线索化结果的正确性
@Test
public void test1(){
threadTreeNode root=new threadTreeNode(1,"a");
threadTreeNode node1=new threadTreeNode(3,"b");
threadTreeNode node2=new threadTreeNode(6,"c");
threadTreeNode node3=new threadTreeNode(8,"d");
threadTreeNode node4=new threadTreeNode(10,"e");
threadTreeNode node5=new threadTreeNode(14,"f");
root.setLeft(node1);
root.setRight(node2);
node1.setLeft(node3);
node1.setRight(node4);
node2.setLeft(node5);
threadBinaryTree tree=new threadBinaryTree(root);
tree.Threaded(root);
System.out.println(node3);
System.out.println(node4);
System.out.println(node5);
System.out.println(node3.getRight());
System.out.println(node4.getLeft());
System.out.println(node4.getRight());
System.out.println(node5.getLeft());
System.out.println(node5.getRight());
}
threadTreeNode{no=8, name='d', leftType=false, rightType=true}
threadTreeNode{no=10, name='e', leftType=true, rightType=true}
threadTreeNode{no=14, name='f', leftType=true, rightType=true}
threadTreeNode{no=3, name='b', leftType=false, rightType=false}
threadTreeNode{no=3, name='b', leftType=false, rightType=false}
threadTreeNode{no=1, name='a', leftType=false, rightType=false}
threadTreeNode{no=1, name='a', leftType=false, rightType=false}
threadTreeNode{no=6, name='c', leftType=false, rightType=false}
3.2 检查遍历结果的正确性
@Test
public void test2(){
threadTreeNode root=new threadTreeNode(1,"a");
threadTreeNode node1=new threadTreeNode(3,"b");
threadTreeNode node2=new threadTreeNode(6,"c");
threadTreeNode node3=new threadTreeNode(8,"d");
threadTreeNode node4=new threadTreeNode(10,"e");
threadTreeNode node5=new threadTreeNode(14,"f");
root.setLeft(node1);
root.setRight(node2);
node1.setLeft(node3);
node1.setRight(node4);
node2.setLeft(node5);
threadBinaryTree tree=new threadBinaryTree(root);
tree.Threaded(root);
tree.infixOrderThreadedBinaryTree();
}
threadTreeNode{no=8, name='d', leftType=false, rightType=true}
threadTreeNode{no=3, name='b', leftType=false, rightType=false}
threadTreeNode{no=10, name='e', leftType=true, rightType=true}
threadTreeNode{no=1, name='a', leftType=false, rightType=false}
threadTreeNode{no=14, name='f', leftType=true, rightType=true}
threadTreeNode{no=6, name='c', leftType=false, rightType=false}