线索二叉树的前序中序后序线索化

13.2 线索化二叉树

在我们构建成一个二叉树的时候,我们发现叶子节点左右节点的指针并没有完全利用上。

如果我们希望充分利用各个节点的左右指针,让各个节点可以指向自己前驱,即父节点,于是线索化二叉树就应运而生

特点

  1. n个节点的二叉链表中含有 n + 1公式 2*n-(n-1) = n + 1个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为“线索”)
  2. 这种加上了线索的二叉链表称为 线索链表,相应的二叉树称为 线索二叉树(Threaded Binary Tree), 根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树、和后续线索二叉树 三种
  3. 一个结点的前一个结点,称为前驱结点
  4. 一个结点的后一个结点,称为后继结点

需求:

​ 将下面的二叉树进行中序线索二叉树。中序遍历的数列为{8, 3, 10, 1, 14, 6}

说明:当线索化二叉树后,Node结点的属性 left 和 right , 存在一下情况:

  1. left 指向的是左子树,也可能是指向的前驱结点,比如 1 结点的 left 指向的左子树,而 10 结点的 left 指向的就是前驱结点
  2. 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 + '\'' +
                '}';
    }

}


前序线索化,后序线索化

注意,前序线索化要添加判断条件,否则就会陷入无限迭代,无法跳出导致栈满,判断条件就是我们声明的 leftTyperightType的值,如果为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 + '\'' +
                '}';
    }

}


posted @ 2021-08-10 14:40  笔锋  阅读(631)  评论(0)    收藏  举报