线索二叉树的前序中序后序线索化
13.2 线索化二叉树
在我们构建成一个二叉树的时候,我们发现叶子节点左右节点的指针并没有完全利用上。
如果我们希望充分利用各个节点的左右指针,让各个节点可以指向自己前驱,即父节点,于是线索化二叉树就应运而生
特点
- n个节点的二叉链表中含有
n + 1公式2*n-(n-1) = n + 1个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为“线索”) - 这种加上了线索的二叉链表称为 线索链表,相应的二叉树称为 线索二叉树(Threaded Binary Tree), 根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树、和后续线索二叉树 三种
- 一个结点的前一个结点,称为前驱结点
- 一个结点的后一个结点,称为后继结点
需求:
将下面的二叉树进行中序线索二叉树。中序遍历的数列为{8, 3, 10, 1, 14, 6}

说明:当线索化二叉树后,Node结点的属性 left 和 right , 存在一下情况:
- left 指向的是左子树,也可能是指向的前驱结点,比如 1 结点的 left 指向的左子树,而 10 结点的 left 指向的就是前驱结点
- right 指向的是右子树,也可能指向的是后继结点,比如 1 节点的 right 指向的是右子树,而 10 节点的 right 指向的就是后继结点
package tree.threadedbinarytree;
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
// 测试 把中序线索二叉树
Node1 root = new Node1(1, "12");
Node1 node2 = new Node1(3, "13");
Node1 node3 = new Node1(6, "16");
Node1 node4 = new Node1(8, "18");
Node1 node5 = new Node1(10, "114");
Node1 node6 = new Node1(14, "114");
// 二叉树后面要递归创建,现在手动创建
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
// 测试线索化
infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree();
infixThreadedBinaryTree.setRoot(root);
infixThreadedBinaryTree.threadeNodes();
// 测试:以10号结点为测试
Node1 leftNode = node5.getLeft();
Node1 rightNode = node5.getRight();
System.out.println("10号结点的前驱结点时:" + leftNode);
System.out.println("10号结点的后继结点" + rightNode);
}
}
// 线索化二叉树 实现了线索化功能的二叉树
class infixThreadedBinaryTree{
private Node1 root;
// 为了实现线索化,需要创建要给指向当前节点的前驱结点的指针
// 在递归的进行线索时,pre 总是保留一个结点
private Node1 pre = null;
// 重载一把threadeNodes方法
public void threadeNodes(){
this.threadedNodes(root);
}
// 编写二叉树进行中序线索化的方法
/**
*
* @param node 就是当前需要线索化的结点
*/
public void threadedNodes(Node1 node){
// 如果 node == null,不能进行线索化
if (node == null){
return;
}
// 1. 先线索化左子树
threadedNodes(node.getLeft());
// 2. 线索化当前结点
// 2.1 先处理当前节点的前驱结点
if (node.getLeft() == null){
// 让当前节点的左指针指向前驱结点
node.setLeft(pre);
// 修改当前节点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
// 2.2 处理当前节点的后继结点
if (pre != null && pre.getRight() == null){
// 让前驱结点的右指针指向当前结点
pre.setRight(node);
// 修改前驱结点的右指针类型
pre.setLeftType(1);
}
// 没处理一个节点后,让当前结点时下一个节点的前驱结点
pre = node;
// 3. 再线索化右子树
threadedNodes(node.getRight());
}
public Node1 getRoot() {
return root;
}
public void setRoot(Node1 root) {
this.root = root;
}
}
// 先创建节点
class Node1{
private int id;
private String name;
private Node1 left;
private Node1 right;
// 说明
// 1. 如果leftType == 0 表示的指向的是左子树,如果1则表示指向前驱结点
// 2. 如果rightType == 0 表示指向的是右子树,如果1则表示指向的是后继结点
private int leftType;
private int rightType;
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 Node1(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node1 getLeft() {
return left;
}
public void setLeft(Node1 left) {
this.left = left;
}
public Node1 getRight() {
return right;
}
public void setRight(Node1 right) {
this.right = right;
}
@Override
public String toString() {
return "Node1{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
前序线索化,后序线索化
注意,前序线索化要添加判断条件,否则就会陷入无限迭代,无法跳出导致栈满,判断条件就是我们声明的 leftType和rightType的值,如果为1,则我们进行递归,如果不为1,则我们不进行递归,否则会进入死循环。
package tree.threadedbinarytree;
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
// 测试 把中序线索二叉树
Node1 root = new Node1(1, "12");
Node1 node2 = new Node1(3, "13");
Node1 node3 = new Node1(6, "16");
Node1 node4 = new Node1(8, "18");
Node1 node5 = new Node1(10, "114");
Node1 node6 = new Node1(14, "114");
// 二叉树后面要递归创建,现在手动创建
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
// 测试中序线索化
infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree();
// infixThreadedBinaryTree.setRoot(root);
// infixThreadedBinaryTree.threadeNodes();
//
// // 测试:以10号结点为测试
// Node1 leftNode = node5.getLeft();
// Node1 rightNode = node5.getRight();
// System.out.println("10号结点的前驱结点时:" + leftNode);
// System.out.println("10号结点的后继结点" + rightNode);
// 测试前序线索化
infixThreadedBinaryTree.setRoot(root);
infixThreadedBinaryTree.postNodes();
// 测试:以10号结点为测试
Node1 leftNode = node5.getLeft();
Node1 rightNode = node5.getRight();
System.out.println("10号结点的前驱结点时:" + leftNode);
System.out.println("10号结点的后继结点" + rightNode);
}
}
// 线索化二叉树 实现了线索化功能的二叉树
class infixThreadedBinaryTree{
private Node1 root;
// 为了实现线索化,需要创建要给指向当前节点的前驱结点的指针
// 在递归的进行线索时,pre 总是保留一个结点
private Node1 pre = null;
// 重载一把threadeNodes方法
public void threadeNodes(){
this.threadedNodes(root);
}
// 编写二叉树进行中序线索化的方法
/**
*
* @param node 就是当前需要线索化的结点
*/
public void threadedNodes(Node1 node){
// 如果 node == null,不能进行线索化
if (node == null){
return;
}
// 1. 先线索化左子树
threadedNodes(node.getLeft());
// 2. 线索化当前结点
// 2.1 先处理当前节点的前驱结点
if (node.getLeft() == null){
// 让当前节点的左指针指向前驱结点
node.setLeft(pre);
// 修改当前节点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
// 2.2 处理当前节点的后继结点
if (pre != null && pre.getRight() == null){
// 让前驱结点的右指针指向当前结点
pre.setRight(node);
// 修改前驱结点的右指针类型
pre.setLeftType(1);
}
// 没处理一个节点后,让当前结点时下一个节点的前驱结点
pre = node;
// 3. 再线索化右子树
threadedNodes(node.getRight());
}
public void preNodes(){
this.preNodes(root);
}
// 前序线索化
public void preNodes(Node1 node){
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){
preNodes(node.getLeft());
}
if (node.getRightType() == 0){
preNodes(node.getRight());
}
}
// 后序线索化
public void postNodes(){
this.postNodes(root);
}
public void postNodes(Node1 node){
if (node == null){
return;
}
postNodes(node.getLeft());
postNodes(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;
}
public void setRoot(Node1 root) {
this.root = root;
}
}
// 先创建节点
class Node1{
private int id;
private String name;
private Node1 left;
private Node1 right;
// 说明
// 1. 如果leftType == 0 表示的指向的是左子树,如果1则表示指向前驱结点
// 2. 如果rightType == 0 表示指向的是右子树,如果1则表示指向的是后继结点
private int leftType;
private int rightType;
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 Node1(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node1 getLeft() {
return left;
}
public void setLeft(Node1 left) {
this.left = left;
}
public Node1 getRight() {
return right;
}
public void setRight(Node1 right) {
this.right = right;
}
@Override
public String toString() {
return "Node1{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

浙公网安备 33010602011771号