线索化二叉树的由来
在二叉树中,我们经常发现,有许多的叶子结点的左右指针没有完全利用上,于是我们希望能将这些指针充分的利用,所以就出现了线索化二叉树
线索化二叉树的基本介绍
1、n个结点的二叉链表中含有n+1个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为线索);
2、这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树,中序以及后序三种;
3、每一个结点的前一个结点称之为前驱结点,后一个结点称为后继结点。
将给定的二叉树进行前序、后序、中序相应的线索化
前序线索化二叉树示意图
中序线索化二叉树示意图
后序线索化二叉树示意图
代码体现如下:
先定义结点:
//定义一个节点
public class TreeNode {
private int num;
private TreeNode left;
private TreeNode right;
//如果leftType=0,则表示指向的是左子树,如果等于1,则表示指向前驱节点
//如果rightType=0,则表示指向的是右子树,如果等于1,则表示指向后继节点
private int leftType;
private int rightType;
private TreeNode parent;
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
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 TreeNode(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"num=" + num +
'}';
}
}
接下来定义树,在树中实现各种序列化:
public class ThreadedBinaryTree {
private TreeNode root;
//为了实现线索化,需要创建当前节点的前驱结点的指针,在递归进行线索化的时候,pre总是保留前一个结点
private TreeNode pre = null;
public void setRoot(TreeNode root) {
this.root = root;
}
/**
* 对二叉树进行前序线索化的方法
*
* @param node 当前需要进行线索化的节点
*/
public void preThreadedNodes(TreeNode node) {
if (node == null) {
return;
}
//线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱结点
node.setLeft(pre);
//修改当前节点的左指针的类型,指向前驱结点
node.setLeftType(1);//1表示指向前驱节点
}
//处理后继节点
if (pre != null && pre.getRight() == null) {
//让前驱结点的右指针指向当前节点
pre.setRight(node);
//修改前驱结点的右指针类型
pre.setRightType(1);//1表示指向后继节点
}
//每处理一个结点后,就让当前结点成为下一个结点的前驱结点,这一点很重要,不然会造成死循环
pre = node;
//线索化左子树
if (node.getLeftType() == 0) {
preThreadedNodes(node.getLeft());
}
//线索化右子树
if (node.getRightType() == 0) {
preThreadedNodes(node.getRight());
}
}
/**
* 对二叉树进行中序线索化的方法
*
* @param node 当前需要进行线索化的节点
*/
public void infixThreadedNodes(TreeNode node) {
if (node == null) {
return;
}
//第一步:线索化左子树
infixThreadedNodes(node.getLeft());
//第二步:线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱结点
node.setLeft(pre);
//修改当前节点的左指针的类型,指向前驱结点
node.setLeftType(1);//1表示指向前驱节点
}
//处理后继节点
if (pre != null && pre.getRight() == null) {
//让前驱结点的右指针指向当前节点
pre.setRight(node);
//修改前驱结点的右指针类型
pre.setRightType(1);//1表示指向后继节点
}
//每处理一个结点后,就让当前结点成为下一个结点的前驱结点,这一点很重要,不然会造成死循环
pre = node;
//第三步:线索化右子树
infixThreadedNodes(node.getRight());
}
/**
* 对二叉树进行后序线索化的方法
*
* @param node 当前需要进行线索化的节点
*/
public void postThreadedNodes(TreeNode node) {
if (node == null) {
return;
}
//线索化左子树
postThreadedNodes(node.getLeft());
//线索化右子树
postThreadedNodes(node.getRight());
//线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱结点
node.setLeft(pre);
//修改当前节点的左指针的类型,指向前驱结点
node.setLeftType(1);//1表示指向前驱节点
}
//处理后继节点
if (pre != null && pre.getRight() == null) {
//让前驱结点的右指针指向当前节点
pre.setRight(node);
//修改前驱结点的右指针类型
pre.setRightType(1);//1表示指向后继节点
}
//每处理一个结点后,就让当前结点成为下一个结点的前驱结点,这一点很重要,不然会造成死循环
pre = node;
}
/**
* 遍历前序线索化二叉树
*/
public void preThreadedList() {
TreeNode node = root;
while (node != null) {
while (node.getLeftType() == 0) {
System.out.println(node);
node = node.getLeft();
}
System.out.println(node);
node = node.getRight();
}
}
/**
* 遍历中序线索化二叉树
*/
public void infixThreadedList() {
//定义一个变量,存储当前遍历的节点,从root开始
TreeNode node = root;
while (node != null) {
//找到leftType=1的结点,找到的第一个即为数值为8的结点,后面会随着遍历而不断变化
//因为当leftType=1时,则说明该节点时按照线索化处理后的有结点
while (node.getLeftType() == 0) {
node = node.getLeft();
}
//打印当前这个结点
System.out.println(node);
//如果当前节点的右指针指向的是后继结点,就一直输出
while (node.getRightType() == 1) {
//获取到当前节点的后继结点
node = node.getRight();
System.out.println(node);
}
//替换掉这个遍历的结点
node = node.getRight();
}
}
/**
* 遍历后序线索化二叉树
*/
public void postThreadedList(TreeNode root) {
if (root.getLeftType() == 0) {
postThreadedList(root.getLeft());
}
if (root.getRightType() == 0) {
postThreadedList(root.getRight());
}
System.out.println(root);
}
}
测试代码:
public class ThreadedBinaryTreeTest {
public static void main(String[] args) {
TreeNode root = new TreeNode(50);
TreeNode node2 = new TreeNode(25);
TreeNode node3 = new TreeNode(75);
TreeNode node4 = new TreeNode(15);
TreeNode node5 = new TreeNode(35);
TreeNode node6 = new TreeNode(65);
TreeNode node7 = new TreeNode(85);
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
node3.setRight(node7);
ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
threadedBinaryTree.setRoot(root);
//测试前序线索化
/*System.out.println("测试前序化线索树:");
threadedBinaryTree.preThreadedNodes(root);
//以node6结点来进行简单测试
System.out.println("node6的前驱结点是 : " + node6.getLeft());
System.out.println("node6的后继结点是 : " + node6.getRight());
//遍历前序线索化二叉树
System.out.println("遍历前序线索化二叉树结果如下:");
threadedBinaryTree.preThreadedList();*/
//测试中序线索化
/*System.out.println("测试中序化线索树:");
threadedBinaryTree.infixThreadedNodes(root);
//以node6结点来进行简单测试
System.out.println("node6的前驱结点是 : " + node6.getLeft());
System.out.println("node6的后继结点是 : " + node6.getRight());
//遍历中序线索化二叉树
System.out.println("遍历中序线索化二叉树结果如下:");
threadedBinaryTree.infixThreadedList();*/
//测试后序线索化
System.out.println("测试后序线索化二叉树:");
threadedBinaryTree.postThreadedNodes(root);
//以node6结点来进行简单测试
System.out.println("node6的前驱结点是 : " + node6.getLeft());
System.out.println("node6的后继结点是 : " + node6.getRight());
//遍历后序线索化二叉树
System.out.println("遍历后序线索化二叉树结果如下:");
threadedBinaryTree.postThreadedList(root);
}
}