线索化二叉树及其遍历
线索二叉树基本介绍
1.利用二叉表中空指针域,存放指向该结点在某种遍历次序下的前驱与后续节点的指针称为线索
2.这种加上了线索的二叉链表称为线索链表,相应的二叉树也称为线索二叉树,根据性质不同分别有前序、中序、后序等线索二叉树
3.一个结点的前一个节点,称为前驱节点
4.一个结点的后一个节点,称为后继节点
应用示例图解分析
中序遍历结果:{8,3,10,1,14,6}进行线索化,该怎么操作?
我们发现[节点八]的前驱节点为Null,后继节点为[节点三],要关联
而[节点三]的前驱节点为[节点八],后继节点为[节点十],但[节点三]已指向两子节点,则无需关联
而[节点十]的前驱节点为[节点三],要关联,后继节点为[节点一],要关联
而[节点一]的前驱节点为[节点十],后继节点为[节点十四],但[节点一]已指向两子节点,则无需关联
而[节点十四]的前驱节点为[节点一],要关联,后继节点为[节点六],要关联
而[节点六]的前驱节点为[节点十四],后继节点为Null,则无需关联
线索化成功的二叉树是下面这个的
left、right属性的情况说明
当线索化二叉树后,Node节点的属性left、right 会有以下情况
①:[left指向的是左子树,也可能是指向的前驱节点],比如[节点一]的left指向的左子树,而[节点十]的left指向的就是前驱节点.
②:[right指向的是右子树,也可能是指向后继节点],比如[节点一]right指向的是右子树,而[节点十]的right指向的是后继节点.
所以我们需要加多[标志Type]代表当前是指向子树、还是节点
中序思路步骤分:先线索化左子树、再线索化当前节点、最后线索化右子树
当我们线索化[某个节点node]时,其实需要一个[前驱节点pre]构成关系关联,这样才有可能实现线索化。
public class ThreadBinaryTree {
private ThreadNode root;
// 为了实现线索化,需要创建要给指向当前节点的前驱节点的指针
// 在进行递归线索化时,pre总是保存前一个节点
private ThreadNode pre;
public static void main(String[] args) {
ThreadNode root = new ThreadNode(1, "tom");
ThreadNode node2 = new ThreadNode(3, "jack");
ThreadNode node3 = new ThreadNode(6, "smith");
ThreadNode node4 = new ThreadNode(8, "mary");
ThreadNode node5 = new ThreadNode(10, "king");
ThreadNode node6 = new ThreadNode(14, "dim");
root.left = node2;
root.right = node3;
node2.left = node4;
node2.right = node5;
node3.left = node6;
ThreadBinaryTree tree = new ThreadBinaryTree();
tree.root = root;
tree.toThreadBinaryTree(root);
ThreadNode left = node5.left;
ThreadNode right = node5.right;
System.out.println(left.no + "---" + left.name);
System.out.println(right.no + "---" + right.name);
System.out.println("中序遍历:");
tree.infixOrder();
}
// 1
// / \
// 3 6
// / \ /
// 8 10 14
// 比如说中序遍历:{8,3,10,1,14,6}
/**
* 1.当前节点为8,线索化左子树,node.left==null,node.left=pre==null,node.leftType=1,前驱节点,pre==null,把当前节点赋给pre,线索化右子树,node.right==null,返回
* 2.当前节点为3,node.left==8不为null,此时pre==8不为null且pre.right == null,则pre.right=3,即是8的后继结点为3,8的rightType类型为1。
* pre=3,线索化3的右子树,node.right=10不为null
* 3.当前节点为10,线索化10的左子树,10的左子树为null,node.left = pre=3,node.leftType = 1;pre为3有右子树。pre=10,线索化10的右子树,10没有右子树
* 4.当前节点为1,node.left!=null,pre=10,pre.right == null,pre.right = 1;pre.rightType = 1;pre=1,线索化1的右子树
* 5.当前节点为6
*
* @param node
*/
public void toThreadBinaryTree(ThreadNode node) {
if (node == null) {
return;
}
// 线索化左子树
toThreadBinaryTree(node.left);
// 处理当前节点的左子树(前驱节点)
if (node.left == null) {
// 当前节点的前驱节点就是前一个节点pre,当前节点左指针指向前驱节点
node.left = pre;
node.leftType = 1;
}
// 处理前一节点的右子树(后继节点)
if (pre != null && pre.right == null) {
// 让前驱节点的右指针指向当前节点
pre.right = node;
pre.rightType = 1;
}
// 处理完当前节点后,让当前节点成为下一个节点的前驱节点
pre = node;
// 线索化右子树
toThreadBinaryTree(node.right);
}
// 中序遍历线索化二叉树
public void infixOrder() {
// 定义一个变量,存储当前遍历的结点,从root开始
ThreadNode node = root;
while (node != null) {
// 循环的找到leftType == 1的结点,第一个找到就是8结点
// 后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化处理后的有效结点
while (node.leftType == 0) {
node = node.left;
}
// 打印当前这个结点
System.out.println(node);
// 如果当前结点的右指针指向的是后继结点,就一直输出,如果不是,则说明是右子树
while (node.rightType == 1) {
node = node.right;
System.out.println(node);
}
// 用右子树替换这个遍历的结点
node = node.right;
}
}
}
class ThreadNode {
public int no;
public String name;
public ThreadNode left;
public ThreadNode right;
/**
* 说明
* 1. leftType == 0 表示指向的是左子树, 1 表示指向的是前驱节点
* 2. rightType == 0 表示指向的是右子树,1 标识指向的是后继节点
*/
public int leftType;
public int rightType;
public ThreadNode(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "ThreadNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
参考:线索化二叉树