Look and think for 15 minutes. Take the clue and store the binary tree

preface

If you think the article is helpful, please leave some praise and collection. Pay attention to xiaoleng and read more dry goods learning articles

★ this is Xiao Leng's blog ‡ see the column for good articles on high-quality technology The official account of the individual, sharing some technical articles, and the pit encountered. Current series: data structure series Source code git warehouse Data structure code address Code Git warehouse address

catalogue

Cued binary tree

The cue binary tree is introduced through a problem

The sequence {1,3,6,8,10,14} is constructed into a binary tree n+1=7

Problem analysis:

  1. When we traverse the above binary tree in middle order, the sequence is {8, 3, 10, 1, 6, 14}
  2. However, the left and right pointers of nodes 6, 8, 10 and 14 are not fully utilized
  3. What if we want to make full use of the left and right pointers of each node so that each node can point to its own front and rear nodes?
  4. Solution - clue binary tree

Basic introduction of clue binary tree

  1. The binary linked list of N nodes contains n+1 [Formula 2n-(n-1)=n+1] empty finger needle fields. The null pointer field in the binary linked list is used to store pointers to the precursor and successor nodes of the node in a certain traversal order (this additional pointer is called "clue")
  2. This binary linked list with clues is called clue linked list, and the corresponding binary tree is called threaded binary tree. According to the different properties of clues, clue binary trees can be divided into three types: pre order clue binary trees, middle order clue binary trees and post order clue binary trees
  3. The previous node of a node is called the precursor node
  4. The next node of a node is called the successor node

Application case of clue binary tree

Put the following binary tree into the middle order clue binary tree. The number sequence traversed in the middle order is {8, 3, 10, 1, 14, 6}

Note: after cueing the binary tree, the attributes left and right of the Node are as follows:

  1. Left refers to the left subtree or the precursor node For example, ① node left points to the left subtree, while ⑩ node left points to the left subtree Is the precursor node
  2. Right refers to the right subtree or the successor node. For example, ① node right refers to the right subtree, while ⑩ node right refers to the successor node

code implementation

package com.hyc.DataStructure.tree.ThreadedBinaryTree;

/**
 * @projectName: DataStructure
 * @package: com.hyc.DataStructure.tree.ThreadedBinaryTree
 * @className: ThreadedBinaryTreeDemo
 * @author: Cold Huanyuan doorwatcher
 * @description: TODO
 * @date: 2022/2/5 16:38
 * @version: 1.0
 */
public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        //Test the function of a medium order clue binary tree
        heroNode root = new heroNode(1, "tom");
        heroNode node2 = new heroNode(3, "jack");
        heroNode node3 = new heroNode(6, "smith");
        heroNode node4 = new heroNode(8, "mary");
        heroNode node5 = new heroNode(10, "king");
        heroNode node6 = new heroNode(14, "dim");

        //The binary tree will be created recursively later. Now it is easy to create it manually
        root.setLeftNode(node2);
        root.setRightNode(node3);
        node2.setLeftNode(node4);
        node2.setRightNode(node5);
        node3.setLeftNode(node6);

        //Sequential cueing in testing
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setHead(root);
        threadedBinaryTree.postThreadedNodes(root);

        //Test: test with node 10
        heroNode leftNode = node5.getLeftNode();
        heroNode rightNode = node5.getRightNode();
        System.out.println("10 The precursor node of node is =" + leftNode); //3
        System.out.println("10 The successor node of node is=" + rightNode); //1

        //When the binary tree is threaded, the original traversal method can be used
        //threadedBinaryTree.infixOrder();
        System.out.println("Traversing the cued binary tree in a cued way");
        threadedBinaryTree.preThreaddeList(); // 8, 3, 10, 1, 14, 6

    }

}

class ThreadedBinaryTree {
    //Determine root node
    private heroNode head;
    //During recursion, the variables used to store the previous node are used to traverse the cued tree and cued nodes
    private heroNode pre;

    public void setHead(heroNode head) {
        this.head = head;
    }

    //Cued traversal
    public void ThreaddeList() {
        //    Here we need a variable to store the current node
        heroNode tempNode = head;
        while (tempNode != null) {
            /* Start loop traversal
             * Loop to find the node with lefttype 1, so the first one to find is the node with node value of 8
             * Later, it will change with traversal. When lefttype ==1 is found, it indicates that the tree has been cued
             * After that, you only need to deal with the effective nodes after cueing
             * */
            while (tempNode.getLefttype() == 0) {
                tempNode = tempNode.getLeftNode();
            }
            //    Because it is a medium order traversal, the order is: left, middle and right. Here, we are from the left lefttype==1, that is, it is the first node at the beginning of the left, so we can output it directly
            System.out.println(tempNode);
            /* If the right pointer of the current node points to the subsequent node, it will be output all the time
             * */
            while (tempNode.getRighttype() == 1) {
                tempNode = tempNode.getRightNode();
                System.out.println(tempNode);
            }
            // Replace this traversal node
            tempNode = tempNode.getRightNode();
        }
    }

    public void preThreaddeList() {
        //    Here we need a variable to store the current node
        heroNode tempNode = head;
        while (tempNode != null) {
            /* Start loop traversal
             * Loop to find the node with lefttype 1, so the first one to find is the node with node value of 8
             * Later, it will change with traversal. When lefttype ==1 is found, it indicates that the tree has been cued
             * After that, you only need to deal with the effective nodes after cueing
             * */
            while (tempNode.getLefttype() == 0) {
                System.out.println(tempNode);
                tempNode = tempNode.getLeftNode();
            }
            //    Because it is a medium order traversal, the order is: left, middle and right. Here, we are from the left lefttype==1, that is, it is the first node at the beginning of the left, so we can output it directly
            System.out.println(tempNode);
            // Traversal of this node
            tempNode = tempNode.getRightNode();
        }


    }
    
    //Cued node
    public void ThreadedNodes(heroNode node) {
        //       Non null judgment
        if (node == null) {
            return;
        }
        //    Cued left subtree
        ThreadedNodes(node.getLeftNode());
        //    Cued node
        //It's not easy to understand. Here's 8 an example
        // 8's left ==null, then 8's lefttype = 1 means that it is a precursor node
        if (node.getLeftNode() == null) {
            //If the left pointer of the current node is null, then point her left pointer to the pre preceding node
            //Taking 8 as an example, when the method pre is empty for the first time, so 8 has no front node, it is the front node itself, so the left pointer state is 1
            node.setLeftNode(pre);
            //    After pointing, change the type to 1
            node.setLefttype(1);
        }
        //Processing successor nodes
        //The leading node judged here is not empty, and the leading node has no successor node
        if (pre != null && pre.getRightNode() == null) {
            //Here, take the latter node of 8 as an example. When the pointer points to 3, the front node pre = 8, which means that the rightnode of 8 is 3
            pre.setRightNode(node);
            //At this time, the post node is not a subtree, and when the right pointer of the pre node points to the node, changing the right state to 1 indicates that it is the successor node
            pre.setRighttype(1);
        }

        //Every time a node is processed, we need to store it in pre, that is, the front node
        //Otherwise, it will cause dead recursion
        pre = node;
        //    Cued right subtree
        ThreadedNodes(node.getRightNode());

    }

    //Cued node
    public void preThreadedNodes(heroNode node) {
        //       Non null judgment
        if (node == null) {
            return;
        }

        //    Cued node
        //It's not easy to understand. Here's 8 an example
        // 8's left ==null, then 8's lefttype = 1 means that it is a precursor node
        if (node.getLeftNode() == null) {
            //If the left pointer of the current node is null, then point her left pointer to the pre preceding node
            //Taking 8 as an example, when the method pre is empty for the first time, so 8 has no front node, it is the front node itself, so the left pointer state is 1
            node.setLeftNode(pre);
            //    After pointing, change the type to 1
            node.setLefttype(1);
        }
        //Processing successor nodes
        //The leading node judged here is not empty, and the leading node has no successor node
        if (pre != null && pre.getRightNode() == null) {
            //Here, take the latter node of 8 as an example. When the pointer points to 3, the front node pre = 8, which means that the rightnode of 8 is 3
            pre.setRightNode(node);
            //At this time, the post node is not a subtree, and when the right pointer of the pre node points to the node, changing the right state to 1 indicates that it is the successor node
            pre.setRighttype(1);
        }

        //Every time a node is processed, we need to store it in pre, that is, the front node
        //Otherwise, it will cause dead recursion
        pre = node;
        if (node.getLefttype() == 0) {
            //    Cued left subtree
            preThreadedNodes(node.getLeftNode());
        }
        if (node.getRighttype() == 0) {
            //    Cued right subtree
            preThreadedNodes(node.getRightNode());
        }
    }

    //Cued node
    public void postThreadedNodes(heroNode node) {
        //       Non null judgment
        if (node == null) {
            return;
        }
        //    Cued left subtree
        ThreadedNodes(node.getLeftNode());
        //    Cued right subtree
        ThreadedNodes(node.getRightNode());
        //    Cued node
        //It's not easy to understand. Here's 8 an example
        // 8's left ==null, then 8's lefttype = 1 means that it is a precursor node
        if (node.getLeftNode() == null) {
            //If the left pointer of the current node is null, then point her left pointer to the pre preceding node
            //Taking 8 as an example, when the method pre is empty for the first time, so 8 has no front node, it is the front node itself, so the left pointer state is 1
            node.setLeftNode(pre);
            //    After pointing, change the type to 1
            node.setLefttype(1);
        }
        //Processing successor nodes
        //The leading node judged here is not empty, and the leading node has no successor node
        if (pre != null && pre.getRightNode() == null) {
            //Here, take the latter node of 8 as an example. When the pointer points to 3, the front node pre = 8, which means that the rightnode of 8 is 3
            pre.setRightNode(node);
            //At this time, the post node is not a subtree, and when the right pointer of the pre node points to the node, changing the right state to 1 indicates that it is the successor node
            pre.setRighttype(1);
        }

        //Every time a node is processed, we need to store it in pre, that is, the front node
        //Otherwise, it will cause dead recursion
        pre = node;


    }

    //   Preorder traversal
    public void preOrder() {
        if (this.head != null) {
            this.head.PreOrder();
        } else {
            System.out.println("Binary tree has no root node and cannot be traversed");
        }
    }

    //Middle order traversal
    public void InfixOrder() {
        if (this.head != null) {
            this.head.infixOrder();
        } else {
            System.out.println("Binary tree has no root node and cannot be traversed");
        }
    }

    //Subsequent traversal
    public void PostOrder() {
        if (this.head != null) {
            this.head.postOrder();
        } else {
            System.out.println("Binary tree has no root node and cannot be traversed");
        }
    }

    //Preorder search
    public heroNode preOrderSearch(int no) {
        if (this.head != null) {
            return this.head.PreOrderSearch(no);
        } else {
            return null;
        }
    }

    //Medium order search
    public heroNode infixOrderSearch(int no) {
        if (this.head != null) {
            return this.head.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    //Sequential search
    public heroNode postOrderSearch(int no) {
        if (this.head != null) {
            return this.head.postOrderSearch(no);
        } else {
            return null;
        }
    }


    //    Delete node
    public void deleteNode(int no) {
        if (head != null) {
            if (head.getId() == no) {
                head = null;
                return;
            } else {
                head.deleteNode(no);
            }
        } else {
            System.out.println("Empty tree,Cannot delete");
        }
    }
}

class heroNode {
    private int id;
    private String name;
    private heroNode leftNode;
    private heroNode rightNode;
    //If the type is 0, it means that there is a subtree. If the type is 1, it means that it is a precursor node / post node
    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 heroNode getLeftNode() {
        return leftNode;
    }

    public void setLeftNode(heroNode leftNode) {
        this.leftNode = leftNode;
    }

    public heroNode getRightNode() {
        return rightNode;
    }

    public void setRightNode(heroNode rightNode) {
        this.rightNode = rightNode;
    }

    public heroNode(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;
    }

    @Override
    public String toString() {
        return "heroNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public void setName(String name) {
        this.name = name;

    }

    //    Preorder traversal
    public void PreOrder() {
        System.out.println(this);
        if (this.getLeftNode() != null) {
            this.leftNode.PreOrder();
        }
        if (this.getRightNode() != null) {
            this.rightNode.PreOrder();
        }
    }

    //    Middle order traversal
    public void infixOrder() {
        if (this.leftNode != null) {
            this.leftNode.infixOrder();
        }
        System.out.println(this);
        if (this.rightNode != null) {
            this.rightNode.infixOrder();
        }
    }

    //   Postorder traversal
    public void postOrder() {
        if (this.leftNode != null) {
            this.leftNode.postOrder();
        }
        if (this.rightNode != null) {
            this.rightNode.postOrder();
        }
        System.out.println(this);
    }

    //   Preorder search
    public heroNode PreOrderSearch(int no) {
        System.out.println("Preorder search");
        //Compare whether the no of the current node is what we want to search
        if (this.id == no) {
            return this;
        }
        //Node to return
        heroNode resNode = null;
        //  Judge whether the node on the left is empty. If it is not empty, recursive preamble search is performed
        //    If found, return the found node
        if (this.leftNode != null) {
            resNode = this.leftNode.PreOrderSearch(no);
        }
        //If it is not null, it means that if it is found on the left, it can be returned directly
        if (resNode != null) {
            return resNode;
        }
        //  Judge whether the node on the right is empty. If it is not empty, recursive preamble search is performed
        //    If found, return the found node
        if (this.rightNode != null) {
            resNode = this.rightNode.PreOrderSearch(no);
        }
        return resNode;
    }

    //   Medium order search
    public heroNode infixOrderSearch(int no) {

        //Node to return
        heroNode resNode = null;
        //  Judge whether the left node is empty. If it is not empty, search in recursive order
        //    If found, return the found node
        if (this.leftNode != null) {
            resNode = this.leftNode.infixOrderSearch(no);
        }
        //If it is not null, it means that if it is found on the left, it can be returned directly
        if (resNode != null) {
            return resNode;
        }
        //Compare whether the no of the current node is what we want to search
        System.out.println("Medium order search");
        if (this.id == no) {
            return this;
        }
        //  Judge whether the node on the right is empty. If it is not empty, search in recursive order
        //    If found, return the found node
        if (this.rightNode != null) {
            resNode = this.rightNode.infixOrderSearch(no);
        }
        return resNode;
    }

    //   Sequential search
    public heroNode postOrderSearch(int no) {

        //Node to return
        heroNode resNode = null;
        //  Judge whether the node on the left is empty. If it is not empty, it will be searched recursively
        //    If found, return the found node
        if (this.leftNode != null) {
            resNode = this.leftNode.postOrderSearch(no);
        }
        //If it is not null, it means that if it is found on the left, it can be returned directly
        if (resNode != null) {
            return resNode;
        }
        //  Judge whether the node on the right is empty. If it is not empty, it will be searched recursively
        //    If found, return the found node
        if (this.rightNode != null) {
            resNode = this.rightNode.postOrderSearch(no);
        }
        //If it is not null, it means that if it is found on the right, it can be returned directly
        if (resNode != null) {
            return resNode;
        }
        System.out.println("Sequential search");
        //If the left and right subtrees are not found, then compare whether the no of the current node is what we want to search
        if (this.id == no) {
            return this;
        }
        return resNode;
    }


    //    delete
    public void deleteNode(int no) {
        //    Traverse to the left. If the left subtree is a little, set the left subtree empty. If not, traverse the right
        if (this.leftNode != null && this.leftNode.id == no) {
            this.leftNode = null;
            return;
        }
        //    Traverse to the right. If the right subtree is a little, set the left subtree empty. If there is no ready left or right, it should be deleted recursively
        if (this.rightNode != null && this.rightNode.id == no) {
            this.rightNode = null;
            return;
        }
        //    If the above two steps are unsuccessful, let's delete recursively to the left first
        if (this.leftNode != null) {
            this.leftNode.deleteNode(no);
        }

        //    If the recursive deletion of the left subtree is not successful, the right subtree will be deleted recursively
        if (this.rightNode != null) {
            this.rightNode.deleteNode(no);
        }
    }
}

Traversal cued binary tree

  1. Description: traverse the binary tree of middle order cueing in front
  2. Analysis: because the direction of each node changes after cueing, the original traversal method cannot be used. At this time, a new traversal method needs to be used Cued binary tree, each node can be traversed by linear way, so there is no need to use recursive way, which also improves the efficiency of traversal. The order of traversal should be consistent with that of middle order traversal.
    public void preThreaddeList() {
        //    Here we need a variable to store the current node
        heroNode tempNode = head;
        while (tempNode != null) {
            /* Start loop traversal
             * Loop to find the node with lefttype 1, so the first one to find is the node with node value of 8
             * Later, it will change with traversal. When lefttype ==1 is found, it indicates that the tree has been cued
             * After that, you only need to deal with the effective nodes after cueing
             * */
            while (tempNode.getLefttype() == 0) {
                System.out.println(tempNode);
                tempNode = tempNode.getLeftNode();
            }
            //    Because it is a medium order traversal, the order is: left, middle and right. Here, we are from the left lefttype==1, that is, it is the first node at the beginning of the left, so we can output it directly
            System.out.println(tempNode);
            // Traversal of this node
            tempNode = tempNode.getRightNode();
        }


    }

Added by Salkcin on Tue, 01 Mar 2022 06:43:06 +0200