package com.xiejianjun.day08.threadBinaryTreeDemo;
/**
* @author bilibilidick
* @version 2022 04
* @date 2022/4/22 17:45
*/
public class ThreadBinaryTreeDemo {
public static void main(String[] args) {
BinaryNode one = new BinaryNode(1, "one");
BinaryNode two = new BinaryNode(2, "two");
BinaryNode three = new BinaryNode(3, "three");
BinaryNode four = new BinaryNode(4, "four");
BinaryNode five = new BinaryNode(5, "five");
one.left = two;
one.right = three;
two.left = four;
three.right = five;
BinaryTree binaryTree = new BinaryTree();
binaryTree.setRoot(one);
// 线索化二叉树
binaryTree.threadInTreeNode(one);
// 遍历线索化之后的二叉树
binaryTree.threadInTreeList(one);
}
}
class BinaryTree {
public BinaryNode root;
public BinaryNode pre = null;
public void setRoot(BinaryNode root) {
this.root = root;
}
public void inRootOrder() {
System.out.println("中序遍历根节点");
if (this.root != null)
this.root.inOrder();
}
public void deleteNodeById(int id) {
System.out.printf("开始删除值为%d的结点", id);
if (this.root == null) {
System.out.println("空树");
return;
}
if (this.root.id == id) {
this.root = null;
} else {
this.root.deleteNode(id);
}
}
public void threadInTreeList(BinaryNode root) {
BinaryNode node = root;
// root本身为空树或者到达最后一个结点退出循环
while (node != null) {
// 当结点有左子树时,继续找其左子树结点,直到找到左子树结点为空的结点,即leftType = 0的结点
while (node != null && node.leftType == 0) {
node = node.left;
}
System.out.println(node);
// 当结点的rightType为1时,代表此结点拥有后继结点,则遍历其后继结点直到rightType为0,即遍历到有右子树结点的结点
while (node != null && node.rightType == 1) {
node = node.right;
System.out.println(node);
}
// 当遍历到有右子树结点的结点时,由于此遍历方式为中序遍历,则将当前遍历的结点用其右子树替换,继续循环遍历即可
node = node.right;
}
}
public void threadInTreeNode(BinaryNode node) {
if (node == null) {
return;
}
// 先中序线索化左子树
threadInTreeNode(node.left);
// 线索化当前结点
// 先处理当前结点的前驱结点,如果当前节点的左子树为空,则让其指向前驱结点,并将leftType置为1
if (node.left == null) {
node.left = pre;
node.leftType = 1;
}
// 因为树是单向的,所以要在下一个结点处判断前一个结点的右子树是否为空,若为空,则将上一个结点的右子树指向自己,即后继结点的线索化
if (pre != null && pre.right == null) {
pre.right = node;
pre.rightType = 1;
}
pre = node;
// 再中序线索化右子树
threadInTreeNode(node.right);
}
}
class BinaryNode {
public int id;
public String name;
public BinaryNode left;
public BinaryNode right;
public int leftType = 0;
public int rightType = 0;
public BinaryNode(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "BinaryNode{" +
"id=" + id +
", left=" + ((left == null)?"无前驱节点":left.id) +
", right=" + ((right == null)?"无后继节点":right.id) +
", leftType=" + leftType +
", rightType=" + rightType +
'}';
}
public void inOrder() {
if (this.left != null && this.leftType == 0) {
this.left.inOrder();
}
System.out.println(this);
if (this.right != null && this.rightType == 0) {
this.right.inOrder();
}
}
public void deleteNode(int id) {
if (this.left != null) {
if (this.left.id == id) {
this.left = null;
return;
} else {
this.left.deleteNode(id);
}
}
if (this.right != null) {
if (this.right.id == id) {
this.right = null;
} else {
this.right.deleteNode(id);
}
}
}
public boolean isLeaf(BinaryNode node) {
return node.left == null && node.right == null;
}
}
关键代码:
public void threadTreeNode(BinaryNode node) {
if (node == null) {
return;
}
// 先中序线索化左子树
threadTreeNode(node.left);
// 线索化当前结点
// 先处理当前结点的前驱结点,如果当前节点的左子树为空,则让其指向前驱结点,并将leftType置为1
if (node.left == null) {
node.left = pre;
node.leftType = 1;
}
// 因为树是单向的,所以要在下一个结点处判断前一个结点的右子树是否为空,若为空,则将上一个结点的右子树指向自己,即后继结点的线索化
if (pre != null && pre.right == null) {
pre.right = node;
pre.rightType = 1;
}
pre = node;
// 再中序线索化右子树
threadTreeNode(node.right);
}
精髓在于由于二叉树单向的特点,需要设置前驱结点,并且利用了第一个结点无须设置前驱结点的特性,绕开了第一层的设置,非常精妙