n个结点的二叉链表中含有n+1 【公式 2n-(n-1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")。
这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
一个结点的前一个结点,称为前驱结点。
一个结点的后一个结点,称为后继结点。
应用案例说明:将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}
package com.atguigu.treedemo.threadtree;
public class ThreadTreeDemo {
public static void main(String[] args) {
TreeNode node1 = new TreeNode(1);
TreeNode node2 = new TreeNode(3);
TreeNode node3 = new TreeNode(6);
TreeNode node4 = new TreeNode(8);
TreeNode node5 = new TreeNode(10);
TreeNode node6 = new TreeNode(14);
node1.setLeft(node2);
node1.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
BinaryTree binaryTree = new BinaryTree(node1);
// binaryTree.threadTree(node1);
// binaryTree.preThreadTree(node1);
binaryTree.posThreadTree(node1);
System.out.println(node4.getLeft());
System.out.println(node4.getRight());
System.out.println(node3.getLeft());
System.out.println(node3.getRight());
// System.out.println("中序遍历线索化二叉树:");
// binaryTree.listThreadTree();
// System.out.println("前序遍历线索化二叉树:");
// binaryTree.preListThreadTree();
}
}
//定义一个二叉树的实现类
class BinaryTree {
private TreeNode root; // 代表树的根节点
private TreeNode pre = null; // 指向前驱节点
// 构造器
public BinaryTree(TreeNode root) {
this.root = root;
}
// 前序遍历线索化二叉树
public void preListThreadTree() {
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 listThreadTree() {
// 定义一个变量存储当前遍历的节点,从root开始
TreeNode node = root;
while(node != null) {
// 循环的找到leftType == 1的节点,第一个找到的就是8节点
// 后面循环着遍历而变化,因为当rightType==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 preThreadTree(TreeNode node) {
// 如果node == null ,此时该节点无法线索化
if(node == null) {
return ;
}
// 先序列化当前节点
if(node.getLeft() == null) {
node.setLeft(pre);
node.setLeftType(1);
}
if(pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
if(node.getLeftType() == 0) {
preThreadTree(node.getLeft());
}
if(node.getRightType() == 0) {
preThreadTree(node.getRight());
}
}
// 中序线索化二叉树
public void threadTree(TreeNode node) {
// 如果node == null 不能线索化
if(node == null) {
return ;
}
// (一) 先线索化左子树
threadTree(node.getLeft());
// (二) 线索化当前节点
// 处理当前节点的前驱节点
// 以8节点来理解
// 8节点的 .left = null , 8节点的 .leftType = 1
if(node.getLeft() == null) {
// 让当前节点的左指针指向前驱节点
node.setLeft(pre);
// 修改当前节点的左指针类型,指向前驱节点
node.setLeftType(1);
}
// 处理后继节点
if(pre != null && pre.getRight() == null) {
// 让前驱节点的右指针指向当前节点
pre.setRight(node);
// 修改前驱节点的右指针类型
pre.setRightType(1);
}
// !!!每处理一个节点后,让当前节点是下一个节点的前驱节点
pre = node;
// (三) 再线索化右子树
threadTree(node.getRight());
}
// 后序线索化二叉树
public void posThreadTree(TreeNode node) {
if(node == null) {
return ;
}
posThreadTree(node.getLeft());
posThreadTree(node.getRight());
if(node.getLeft() == null) {
node.setLeft(pre);
node.setLeftType(1);
}
if(pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
}
}
//定义一个节点类
class TreeNode {
private int data; // 用来存放节点数据
private TreeNode right; // 指向该节点的右子树
private TreeNode left; // 指向该节点的左子树
/*
* leftType 为0代表的是指向的为左子树,leftType为1代表的是指向前前驱节点或者是后继节点
*/
private int leftType; // 用来标明左子树的指向类型
private int rightType; // 用来标明右子树的指向类型
// 构造器
public TreeNode(int data) {
this.data = data;
}
/*
* 定义属性的get和set方法
*/
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public int getData() {
return data;
}
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;
}
@Override
public String toString() {
return "TreeNode [data=" + data + "]";
}
}
对于二叉树的后续遍历为什么没写?因为后续遍历线索二叉树时还需要一个指针指向parent,逻辑相对复杂。线索二叉树的优势无非就是提高了遍历的效率,所以能掌握前中遍历就可以了。当前你要追求更高的境界更好。