Binary tree (sequential storage binary tree, threaded binary tree, Huffman tree, binary sort tree, balanced binary tree)

Here I will not explain some basic terms of the tree. Partners in need can go to Baidu to understand the terms of the tree, such as nodes, weights, etc.

There are three ways to traverse the tree: pre order traversal, middle order traversal and post order traversal.

In traversal, we mainly use recursion to realize our traversal.

Preorder traversal: first output the parent node, and then traverse the left and right subtrees

Medium order traversal: first traverse the left subtree, output the parent node, and then traverse the right subtree

Post order traversal: first traverse the left subtree, then traverse the right subtree, and finally output the parent node.

  For example, as shown in the figure above

The preorder traversal result is: 5   three   two   four   six

The middle order traversal result is: 2   three   four   five   six

The subsequent traversal results are: 2   four   three   six   five

Summary: it mainly depends on the output order of the parent node to determine which traversal order.

Here simply attach the code for traversal before, during and after the sequence, and do not repeat it too much.

/**
     * Preorder traversal
     * Preorder traversal: first output the parent node, and then traverse the left and right subtrees
     */
    public void preOrder() {
        System.out.println(this);//Output parent node first
        if (this.left != null) {//Left is not empty
            this.left.preOrder();//Left recursion
        }
        //After traversing to the left, go to the right
        if (this.right != null) {
            this.right.preOrder();//Recursive right
        }

    }

    /**
     * Medium order traversal
     * Medium order traversal: first traverse the left subtree, then output the parent node, and then traverse the right subtree
     */
    public void midOrder() {
        if (this.left != null) {//Left is not empty
            //Left recursion
            this.left.midOrder();
        }
        System.out.println(this);//Output parent node
        //Traverse right
        if (this.right != null) {
            this.right.midOrder();//Recursive right
        }

    }

    /**
     * Subsequent traversal
     * Subsequent traversal: first traverse the left subtree, then traverse the right subtree, and finally output the parent node
     */
    public void suffixOrder() {
        if (this.left != null) {//Left is not empty
            //Left recursion
            this.left.suffixOrder();
        }
        //Traverse right
        if (this.right != null) {
            this.right.suffixOrder();//Recursive right
        }
        System.out.println(this);//Output parent node

    }

Now let's get to the point.

catalogue

Query and deletion of binary tree

Sequential storage binary tree

Cued binary tree

huffman tree

Binary sort tree (BST)

Balanced binary tree (AVL)

summary

Query and deletion of binary tree

Binary tree: a form in which each node can have at most two child nodes becomes a binary tree. The child nodes of binary tree are divided into left node and right node.

Full binary tree: if all leaf nodes of the binary tree are in the last layer, and the total number of nodes = 2^n -1, n is the number of layers, we call it full binary tree.

(this picture is from Mr. Han's picture)

Complete binary tree: if all leaf nodes of the binary tree are in the last or penultimate layer, and the leaf nodes of the last layer are continuous on the left, and the leaf nodes of the penultimate layer are somewhat continuous, we call it a complete binary tree.

(this picture is from Mr. Han's picture)

Here we will realize the traversal, deletion and search of binary tree;

The traversal here is the so-called pre, middle and post order traversal. The search and deletion are also realized with the help of the pre, middle and post traversal.

Traversal has been mentioned above, so I won't repeat it.

Next, let's look at the binary tree search:

Here I will take the previous order traversal search as an example. The middle order traversal search is similar to the post order traversal search. You can try to complete it by yourself, and the code will be posted.

  Code attached:

 /**
     * Preorder traversal search
     *
     * @param id id to find
     * @return If null, there is no such data. If there is, the node object is returned
     */
    public Node preOrderSearch(int id) {
        if (this.id == id) {//At this time, find the corresponding node object and directly return to the current object
            return this;
        }
        Node temp = null;//Record results
        if (this.left != null) {
            temp = this.left.preOrderSearch(id);//Recursive search to the left and return the result
        }
        if (temp != null) {
            //If temp is not empty, it means that the value to be searched is found in the left subtree and returned directly
            return temp;
        }
        if (this.right != null) {//When the above temp is not returned, it indicates that the left subtree does not find a value and enters the right subtree
            temp = this.right.preOrderSearch(id);//Right recursive lookup
        }
        return temp;//temp is returned when the current recursion ends.

    }

Deletion of binary tree: because binary tree is one-way specified, which is a bit similar to single linked list, we need to find the parent node of the deleted node to delete it, otherwise it cannot be deleted.

Here we delete a provision:

1) If the node to be deleted is a leaf node, it will be deleted directly.

2) If the node to be deleted has two child nodes, we will delete the node and replace the node with its right child node.

3) If the node to be deleted has only one child node, we will replace the deleted node with its child node.

This is mainly to find the parent node of the node to be deleted. Because it is relatively simple, I won't go into details, so I'll write the code directly.

/**
     * Delete leaf node or subtree
     * This is similar to the single linked list. We need to locate the last node to delete
     *
     * @param id
     */
    public void delete(int id) {
        if (this.left != null) {//If the left node of the current node is not empty
            if (this.left.id == id) {
                if (this.left.left == null && this.left.right == null) {//When the currently deleted node is a leaf node, it will be deleted directly
                    this.left = null;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.left.left != null && this.left.right == null) {
                    //If the node to be deleted is a subtree, its right node is empty and its left node is not empty, its left node will replace its position
                    this.left = this.left.left;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.left.left == null && this.left.right != null) {
                    //If the node to be deleted is a subtree, its left node is empty and its right node is not empty, its right node will replace its position
                    this.left = this.left.right;
                    System.out.println("Delete succeeded");
                    return;
                } else {
                    //If the left and right nodes are not empty, the left node will replace the current position
                    this.left.left.right = this.left.right;
                    this.left = this.left.left;
                    System.out.println("Delete succeeded");
                    return;
                }
            } else {
                //If the left node of the current node is not the node to be deleted, it will be deleted recursively to the left
                this.left.delete(id);
            }
        }
        if (this.right != null) {
            if (this.right.id == id) {
                if (this.right.left == null && this.right.right == null) {//When the currently deleted node is a leaf node, it will be deleted directly
                    this.right = null;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.right.left != null && this.right.right == null) {
                    //If the node to be deleted is a subtree, its right node is empty and its left node is not empty, its left node will replace its position
                    this.right = this.right.left;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.right.left == null && this.right.right != null) {
                    //If the node to be deleted is a subtree, its left node is empty and its right node is not empty, its right node will replace its position
                    this.right = this.right.right;
                    System.out.println("Delete succeeded");
                    return;
                } else {
                    //If the left and right nodes are not empty, the left node will replace the current position
                    this.right.left.right = this.right.right;
                    this.right = this.right.left;
                    System.out.println("Delete succeeded");
                    return;
                }
            } else {
                //If the right node of the current node is not the node to be deleted, it will be deleted recursively to the right
                this.right.delete(id);
            }
        }


    }

Sequential storage binary tree

Sequential storage binary tree: (sequential storage binary tree only considers complete binary tree) sequential storage binary tree refers to the mutual transformation between binary tree and array. As shown in the figure below, it is a xian

Features of sequential storage binary tree:

1. Sequential binary tree usually only considers complete binary tree

2. The left child node of the nth element is 2   *  n   + one

3. The right child node of the nth element is 2   *  n   + two

4. The parent node of the nth element is (n-1)/   two

5.n: indicates the number of elements in the binary tree (starting with 0, as shown in the figure)

In fact, to put it bluntly, sequential storage binary tree is an array tree, but we do not create nodes to construct the tree, but directly traverse it through the array subscript. The key point is the formula of the above left child node, right child node and parent node.

Code attachment (including pre -, middle - and post order search):

package com.liu.tree;

/**
 * @author liuweixin
 * @create 2021-09-14 15:06
 */
//Sequential storage binary tree
public class ArrBinaryTree {
    private int[] arr;

    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }

    public static void main(String[] args) {
        int[] arr = new int[7];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
        arrBinaryTree.preOrder();//Preorder traversal: 1 2 4 5 3 6 7
        System.out.println("======================");
        arrBinaryTree.midOrder();//Middle order traversal: 4 2 5 1 6 3 7
        System.out.println("======================");
        arrBinaryTree.suffixOrder();//Post order traversal: 4 5 2 6 7 3 1
    }

    /**
     * Traversing storage binary tree in encapsulation overload preamble
     */
    public void preOrder() {
        preOrder(0);
    }
    /**
     * Sequential traversal storage binary tree in encapsulation overload
     */
    public void midOrder() {
        midOrder(0);
    }
    /**
     * Traversing storage binary tree after encapsulation overload
     */
    public void suffixOrder() {
        suffixOrder(0);
    }

    /**
     * Preorder traversal sequential storage binary tree
     *
     * @param index Subscript of array
     */
    public void preOrder(int index) {
        if (arr == null || arr.length == 0) {
            System.out.println("The sequential storage binary tree is empty");
            return;
        }
        //Output the current node first
        System.out.println(arr[index]);
        //Left recursion
        if ((2 * index + 1) < arr.length) {//When the subscript value is less than the length of the array, it indicates that there is a value there
            preOrder(2 * index + 1);//Left recursion
        }
        if ((2 * index + 2) < arr.length) {
            //Judge whether the right child node has a value
            preOrder(2 * index + 2);
        }
    }

    /**
     * Middle order traversal sequential storage binary tree
     * @param index  Subscript of array
     */
    public void midOrder(int index) {
        if (arr == null || arr.length == 0) {
            System.out.println("The sequential storage binary tree is empty");
            return;
        }
        //Recursion to the left -- > and then to the output node -- > recursion to the right
        if ((2 * index + 1) < arr.length) {
            midOrder(2 * index + 1);
        }
        System.out.println(arr[index]);
        if ((2 * index + 2) < arr.length) {
            midOrder(index * 2 + 2);//Recursive right
        }
    }
    /**
     * The subsequent traversal sequence stores the binary tree
     * @param index  Subscript of array
     */
    public void suffixOrder(int index) {
        if (arr == null || arr.length == 0) {
            System.out.println("The sequential storage binary tree is empty");
            return;
        }
        //Recursion to the left -- > and then to the output node -- > recursion to the right
        if ((2 * index + 1) < arr.length) {
            suffixOrder(2 * index + 1);
        }
        if ((2 * index + 2) < arr.length) {
            suffixOrder(index * 2 + 2);//Recursive right
        }
        System.out.println(arr[index]);
    }
}

Cued binary tree

When we traverse the binary tree, we will find that the left and right pointers of leaf nodes or subtrees with only one leaf node are not fully utilized; Therefore, we hope to make full use of the left and right pointers of each node so that each pointer can point to its own front and rear nodes. This leads to the clue binary tree.

The binary list of N nodes contains n+1 (2n-(n-1)=n+1) empty finger 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")

The former node of a node is called the precursor node; the latter node of a node is called the successor node.

Due to the different traversal order, the direction of its predecessor and successor nodes is different. Therefore, the cued binary tree can be divided into: pre order cued binary tree, middle order cued binary tree and post order cued binary tree.

The key points of creating a cued binary tree (medium order cued binary tree as an example) are:

When processing the predecessor node, judge whether the left node of the current node is empty. If it is empty, point the left node to the pre node (the pre node refers to the previous node of the current traversal). When processing the successor node, first assign the current node to the pre node,   Then traverse the next node. When the next node, judge that pre! = null & & pre. Right! = null. At this time, the successor node of pre right=node (current node), so as to complete the assignment of the predecessor and successor nodes.  

Code attachment (including pre order clue binary tree, middle order clue binary tree and subsequent clue binary tree):

package com.liu.tree;


/**
 * @author liuweixin
 * @create 2021-09-14 19:33
 */
//Cued binary tree
public class ThreadedBinaryTree {
    Node1 pre;//Record the previous node of the current node
    Node1 root;

    public void setRoot(Node1 root) {
        this.root = root;
    }

    public static void main(String[] args) {
        Node1 root = new Node1(1, "lwx");
        Node1 node3 = new Node1(3, "lwx");
        Node1 node6 = new Node1(6, "lwx");
        Node1 node8 = new Node1(8, "lwx");
        Node1 node10 = new Node1(10, "lwx");
        Node1 node14 = new Node1(14, "lwx");
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);
        root.left = node3;
        root.right = node6;
        node3.left = node8;
        node3.right = node10;
        node6.left = node14;
//        threadedBinaryTree.midThreadedNodes();
//        System.out.println("traversing ordered threaded binary tree");
//        threadedBinaryTree.midShow();
        threadedBinaryTree.preThreadedNodes();
        System.out.println("Traversing preordered threaded binary tree");
        threadedBinaryTree.preShow();
    }

    /**
     * Encapsulate the method of middle order cueing
     */
    public void midThreadedNodes() {
        midThreadedNodes(root);
    }

    /**
     * Encapsulate the method of preorder cueing
     */
    public void preThreadedNodes() {
        preThreadedNodes(root);
    }

    /**
     * Cue incoming nodes
     * Preordered cued binary tree
     *
     * @param node1 Nodes to be threaded
     */
    public void preThreadedNodes(Node1 node1) {
        if (node1 == null) {//If the node is empty, there is no normalization
            return;
        }
        //Cue the current node
        if (node1.left == null) {//Only when the left node is empty can it be cued. After cueing, it points to the precursor node
            node1.left = pre;
            node1.leftType = 1;//Then modify the type of its left child node
        }
        if (pre != null && pre.right == null) {
            //Only when the right child node of the previous node is empty can it be cued, and its subsequent nodes point to the current node
            pre.right = node1;
            pre.rightType = 1;
        }
        //After each node is processed, the current node is the precursor node of the next node
        pre = node1;
        //Left recursion
        if (node1.leftType == 0) {//Judge whether the left node of the current node is a left subtree. If so, it will enter recursion; if not, it will not enter recursion (otherwise, dead recursion will occur)
            preThreadedNodes(node1.left);
        }
        //Recursive right
        if (node1.rightType == 0) {//Judge whether the right node of the current node is a right subtree. If so, it will enter recursion; if not, it will not enter recursion (otherwise, dead recursion will occur)
            preThreadedNodes(node1.right);
        }

    }

    /**
     * Cue incoming nodes
     * Medium order cued binary tree
     *
     * @param node1 Nodes to be threaded
     */
    public void midThreadedNodes(Node1 node1) {
        if (node1 == null) {
            //If the node is empty, there is no normalization
            return;
        }
        //Cue left subtree first
        midThreadedNodes(node1.left);
        //Cue the current node
        if (node1.left == null) {
            //Only when the left node is empty can it be cued. After cueing, it points to the precursor node
            node1.left = pre;
            //Then modify the type of its left child node
            node1.leftType = 1;
        }
        if (pre != null && pre.right == null) {
            //Only when the right child node of the previous node is empty can it be cued, and its subsequent nodes point to the current node
            pre.right = node1;
            pre.rightType = 1;
        }
        //After each node is processed, the current node is the precursor node of the next node
        pre = node1;
        //Cued right subtree
        midThreadedNodes(node1.right);

    }

    /**
     * Traversal clue binary tree (preorder traversal)
     */
    public void preShow() {
        Node1 node1 = root;
        if (node1 == null) {
            System.out.println("The number is empty and cannot be traversed");
            return;
        }
        System.out.println(node1);//Output the current node first
        while (node1 != null) {//As long as the current node is not empty, you can enter the loop traversal
            while (node1.leftType == 0) {//Find the left leaf node first
                node1 = node1.left;//Add left
                System.out.println(node1);//Output current node
            }
            while (node1.rightType == 1) {//Find the successor node of the left child node
                node1 = node1.right;//Progressive right
                System.out.println(node1);//Output current node
            }
            //When the loop ends, the subsequent nodes of the left leaf node have been traversed
            //Then the current node continues to traverse to the right
            node1 = node1.right;
        }
    }

    /**
     * Traversal clue binary tree (medium order traversal)
     */
    public void midShow() {
        Node1 node1 = root;
        if (node1 == null) {
            System.out.println("The number is empty and cannot be traversed");
            return;
        }
        while (node1 != null) {//As long as the current node is not empty, you can enter the loop traversal
            while (node1.leftType == 0) {//Find the left leaf node first
                node1 = node1.left;//Add left
            }
            System.out.println(node1);//At this time, the left leaf node has been found and output directly
            while (node1.rightType == 1) {//Find the successor node of the left child node
                node1 = node1.right;//Progressive right
                System.out.println(node1);//Output current node
            }
            //When the loop ends, the subsequent nodes of the left leaf node have been traversed
            //Then the current node continues to traverse to the right (because the middle order traversal: the order of left - > Middle - > right)
            node1 = node1.right;
        }
    }
}

//Create a node class
class Node1 {
    int id;
    String name;
    Node1 left;//Left node
    Node1 right;//Right node
    int leftType;//1 refers to the precursor node, and 0 refers to the left subtree
    int rightType;//1 is the successor node, and 0 is the right subtree

    public Node1(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void setLeft(Node1 left) {
        this.left = left;
    }

    public void setRight(Node1 right) {
        this.right = right;
    }

    /**
     * Preorder traversal search
     *
     * @param id id to find
     * @return If null, there is no such data. If there is, the node object is returned
     */
    public Node1 preOrderSearch(int id) {
        if (this.id == id) {//At this time, find the corresponding node object and directly return to the current object
            return this;
        }
        Node1 temp = null;//Record results
        if (this.left != null) {
            temp = this.left.preOrderSearch(id);//Recursive search to the left and return the result
        }
        if (temp != null) {
            //If temp is not empty, it means that the value to be searched is found in the left subtree and returned directly
            return temp;
        }
        if (this.right != null) {//When the above temp is not returned, it indicates that the left subtree does not find a value and enters the right subtree
            temp = this.right.preOrderSearch(id);//Right recursive lookup
        }
        return temp;//temp is returned when the current recursion ends.

    }

    /**
     * Middle order traversal search
     *
     * @param id id to find
     * @return Return node object
     */
    public Node1 midOrderSearch(int id) {
        Node1 temp = null;//Record results
        if (this.left != null) {//Judge whether the current node is empty. If it is not empty, search in recursive middle order
            temp = this.left.midOrderSearch(id);//Recursive search to the left and return the result
        }
        if (temp != null) {
            //If temp is not empty, it means that the value to be searched is found in the left subtree and returned directly
            return temp;
        }
        if (this.id == id) {//If found, returns the current object
            return this;
        }
        if (this.right != null) {//When the above temp is not returned and not found, it indicates that no value is found in the left subtree and enters the right subtree
            temp = this.right.midOrderSearch(id);//Right recursive lookup
        }
        return temp;//At this time, temp is returned no matter the right loop cannot find the value.
    }


    /**
     * Subsequent traversal search
     *
     * @param id id to find
     * @return Return node object
     */
    public Node1 suffixOrderSearch(int id) {
        Node1 temp = null;//Record results
        if (this.left != null) {//Judge whether the current node is empty. If it is not empty, search in recursive middle order
            temp = this.left.suffixOrderSearch(id);//Recursive search to the left and return the result
        }
        if (temp != null) {
            //If temp is not empty, it means that the value to be searched is found in the left subtree and returned directly
            return temp;
        }
        if (this.right != null) {//When the above temp is not returned and not found, it indicates that no value is found in the left subtree and enters the right subtree
            temp = this.right.suffixOrderSearch(id);//Right recursive lookup
        }
        if (temp != null) {
            //If temp is not empty, it means that the value to be searched is found in the right subtree and returned directly
            return temp;
        }
        if (this.id == id) {//If found, returns the current object
            return this;
        }
        return temp;

    }

    /**
     * Preorder traversal
     * Preorder traversal: first output the parent node, and then traverse the left and right subtrees
     */
    public void preOrder() {
        System.out.println(this);//Output parent node first
        if (this.left != null) {//Left is not empty
            this.left.preOrder();//Left recursion
        }
        //After traversing to the left, go to the right
        if (this.right != null) {
            this.right.preOrder();//Recursive right
        }

    }

    /**
     * Medium order traversal
     * Medium order traversal: first traverse the left subtree, then output the parent node, and then traverse the right subtree
     */
    public void midOrder() {
        if (this.left != null) {//Left is not empty
            //Left recursion
            this.left.midOrder();
        }
        System.out.println(this);//Output parent node
        //Traverse right
        if (this.right != null) {
            this.right.midOrder();//Recursive right
        }

    }

    /**
     * Subsequent traversal
     * Subsequent traversal: first traverse the left subtree, then traverse the right subtree, and finally output the parent node
     */
    public void suffixOrder() {
        if (this.left != null) {//Left is not empty
            //Left recursion
            this.left.suffixOrder();
        }
        //Traverse right
        if (this.right != null) {
            this.right.suffixOrder();//Recursive right
        }
        System.out.println(this);//Output parent node

    }

    /**
     * Delete leaf node or subtree
     * This is similar to the single linked list. We need to locate the last node to delete
     *
     * @param id
     */
    public void delete(int id) {
        if (this.left != null) {//If the left node of the current node is not empty
            if (this.left.id == id) {
                if (this.left.left == null && this.left.right == null) {//When the currently deleted node is a leaf node, it will be deleted directly
                    this.left = null;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.left.left != null && this.left.right == null) {
                    //If the node to be deleted is a subtree, its right node is empty and its left node is not empty, its left node will replace its position
                    this.left = this.left.left;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.left.left == null && this.left.right != null) {
                    //If the node to be deleted is a subtree, its left node is empty and its right node is not empty, its right node will replace its position
                    this.left = this.left.right;
                    System.out.println("Delete succeeded");
                    return;
                } else {
                    //If the left and right nodes are not empty, the left node will replace the current position
                    this.left.left.right = this.left.right;
                    this.left = this.left.left;
                    System.out.println("Delete succeeded");
                    return;
                }
            } else {
                //If the left node of the current node is not the node to be deleted, it will be deleted recursively to the left
                this.left.delete(id);
            }
        }
        if (this.right != null) {
            if (this.right.id == id) {
                if (this.right.left == null && this.right.right == null) {//When the currently deleted node is a leaf node, it will be deleted directly
                    this.right = null;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.right.left != null && this.right.right == null) {
                    //If the node to be deleted is a subtree, its right node is empty and its left node is not empty, its left node will replace its position
                    this.right = this.right.left;
                    System.out.println("Delete succeeded");
                    return;
                } else if (this.right.left == null && this.right.right != null) {
                    //If the node to be deleted is a subtree, its left node is empty and its right node is not empty, its right node will replace its position
                    this.right = this.right.right;
                    System.out.println("Delete succeeded");
                    return;
                } else {
                    //If the left and right nodes are not empty, the left node will replace the current position
                    this.right.left.right = this.right.right;
                    this.right = this.right.left;
                    System.out.println("Delete succeeded");
                    return;
                }
            } else {
                //If the right node of the current node is not the node to be deleted, it will be deleted recursively to the right
                this.right.delete(id);
            }
        }


    }

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

huffman tree

Given n weights as n leaf nodes, a binary tree is constructed. If the weighted path length (wpl) of the tree reaches the minimum, such a binary tree is called the optimal binary tree, also known as Huffman tree. Huffman tree is the tree with the shortest weighted path length, and the node with larger weight is closer to the root.

Weighted path length of node (wpl): the path length from the root node to the node multiplied by the weight of the node.

Weighted path length of tree: the sum of weighted path lengths of all leaf nodes.

The wpl smallest tree is the Huffman tree.

Construction idea of Huffman tree:

  1. Create node objects respectively from the data of the array and store them in ArrayLists (hereinafter referred to as list)
  2. Sort the list first.
  3. Create a parent node object. The weight of the parent node object is the sum of the weights of the first two data (i.e. the smallest two data) of the list, and then point the left and right nodes of the parent node to the two data respectively.
  4. Store the parent node in the list, and delete the two child nodes in the list corresponding to the parent node
  5. Repeat the above steps, and finally there will be only one data left in the list, which is the root node of the Huffman tree.

Here is also a Huffman tree encoding and decoding, interested friends can go and learn about it.

Code attached:

package com.liu.treealgorithm;

import java.util.ArrayList;
import java.util.Collections;

/**
 * @author liuweixin
 * @create 2021-09-16 22:29
 */
public class HuffmanTree {
    public static void main(String[] args) {
        int[]arr = new int[]{3,13,29,1,7,8,6};
        HuffmanTree huffmanTree = new HuffmanTree();
        Node root = huffmanTree.GetHuffmanTree(arr);
        root.preOrder();
    }

    /**
     * Form Huffman tree
     * @param arr An array to form a Huffman tree
     * @return  Returns the root node of the Huffman tree
     */
    public Node GetHuffmanTree(int[] arr) {
        if (arr == null) {
            throw new RuntimeException("The passed in array is empty");
        }
        ArrayList<Node> lists = new ArrayList<>();//Create a list to store node data
        for (int data : arr) {
            lists.add(new Node(data));//Add to lists collection
        }
        while (lists.size()>1) {//When the data length in lists is greater than 1, it can still be added to Huffman tree
            Collections.sort(lists);//Sort the data in lists before operating on the lists collection
            Node parent = new Node(lists.get(0).value + lists.get(1).value);//The weight of the parent node is equal to the weight of its two child nodes
            //Set the node point of the parent node
            parent.left=lists.get(0);
            parent.right=lists.get(1);
            //Then add it to the lists collection
            lists.add(parent);
            //And delete the merged child nodes in the lists set
            lists.remove(0);
            lists.remove(0);//Both times are 0, because after the above removal operation, the subscript of the next node moves forward by one bit, so it is still 0
        }
        //At the end of the loop, there is a node left in the lists set, that is, the root node, which is returned
        return lists.get(0);
    }
}

class Node implements Comparable {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    @Override
    public int compareTo(Object o) {
        Node node = (Node) o;
        return this.value - node.value;
    }

    /**
     * Preorder traversal operation
     */
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
}

Binary sort tree (BST)

Binary sort tree: that is, for any non leaf node of a binary tree, its left child node is smaller than the current node and its right child node is larger than the current node. Special note: if you have the same value, you can place the node to the left child node or the right child node. In our previous drawings, all are binary sort trees.

Ideas for adding nodes:

1. The weight of the incoming node is compared with that of the current node.

1.1. If it is less than the weight of the current node, judge whether the left node of the current node is empty.

1.1.1 if it is empty, the left child node of the current node directly points to the incoming node;

1.1.2 if it is not empty, continue to recurse to the left;

1.2. If it is greater than the weight of the current node, judge whether the right node of the current node is empty

1.2.1 if it is empty, the right child node of the current node is the node directly passed in;

1.2.2 if it is not empty, continue to recurse to the right.

Code attached:

/**
     * Add node
     *
     * @param node1 Node to add
     */
    public void addNode(Node1 node1) {
        if (node1 == null) {
            return;
        }
        if (node1.id < this.id) {//If the id value of the incoming node is smaller than the id value of the node that is currently adjusting the method,
            if (this.left == null) {
                this.left = node1;
            } else {//If it is not empty, it is added recursively
                this.left.addNode(node1);
            }
        } else {//If the id value of the incoming node is larger or equal to the id value of the node that is currently adjusting the method.
            if (this.right == null) {
                this.right = node1;
            } else {
                //Recursive addition
                this.right.addNode(node1);
            }
        }
    }

Ideas for deleting nodes:

Note: This is similar to a single linked list. We need to find the parent node of this node before deleting it.
1. Delete leaf node.
(1) You need to find the targetNode to delete first
(2) Find the parent node of targetNode
(3) Determines whether the targetNode is the left child node or the right child node of the parent
(4) Delete according to the previous situation   
      Left child node parent.left=null;
      Right child node parent.right=null;
2. Delete nodes with only one subtree.
(1) First, find the targetNode to delete
(2) Find the parent node of targetNode
(3) Determines whether the child node of the targetNode is a left child node or a right child node
(4) Is the targetnode the left child node or the right child node of the parent
(5) If the targetNode has a left child node
  5.1 if targetNode is the left child node of parent
  parent.left=targetNode.left
  5.2 if the targetNode is the right child node of the parent
   parent.right=targetNode.left
(6) If the targetNode has a right child node
6.1 if targetNode is the left child node of parent
     parent.left=targetNode.right;
6.2 if the targetNode is the right child node of the parent
     parent.right=targetNode.right;
3. Delete a node with two subtrees.
(1) First, find the targetNode to delete
(2) Find the parent node of targetNode
(3) Find the smallest node from the right subtree of targetNode
(4) Use a temporary variable to save the value of the minimum node to temp
(5) Delete the minimum node

The complete code is attached:

package com.liu.treealgorithm;

/**
 * @author liuweixin
 * @create 2021-09-17 11:07
 */
//Sorted binary tree
public class BinarySortTree {
    Node1 root;

    public static void main(String[] args) {
        Node1 root = new Node1(7);
        Node1 node3 = new Node1(3);
        Node1 node1 = new Node1(1);
        Node1 node5 = new Node1(5);
        Node1 node10 = new Node1(10);
        Node1 node9 = new Node1(9);
        Node1 node12 = new Node1(12);
        Node1 node11 = new Node1(11);
        Node1 node2 = new Node1(2);
        BinarySortTree binarySortTree = new BinarySortTree();
        binarySortTree.add(root);
        binarySortTree.add(node3);
        binarySortTree.add(node1);
        binarySortTree.add(node5);
        binarySortTree.add(node10);
        binarySortTree.add(node9);
        binarySortTree.add(node12);
        binarySortTree.add(node11);
        binarySortTree.add(node2);
        //Medium order traversal
        System.out.println("Before deletion:");
        binarySortTree.root.midOrder();//1 2 3 5 7 9 10 11 12
        System.out.println("After deletion:");
        binarySortTree.delNode(3);
        binarySortTree.root.midOrder();
    }

    //Find the node to delete
    public Node1 search(int id) {
        if (root == null) {
            return null;
        } else {
            return root.search(id);
        }
    }

    //Find the parent node of the node to delete
    public Node1 searchParent(int id) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(id);
        }
    }

    /**
     * Returns the value of the smallest node of a binary sort tree with node as the root node
     * Delete the smallest node of the binary sort tree whose node is the root node
     *
     * @param node1
     * @return
     */
    public int delRightTreeMin(Node1 node1) {
        Node1 target = node1;
        //Loop through the left child node to find the minimum value
        while (target.left != null) {
            target = target.left;
        }
        //This is the target, which points to the smallest node
        //Delete minimum node
        delNode(target.id);
        return target.id;

    }

    //Delete Vertex 
    public void delNode(int id) {
        if (root == null) {
            return;
        } else {
            //1. First find the targetNode to delete
            Node1 targetNode = search(id);
            //If the node to be deleted is not found
            if (targetNode == null) {
                return;
            }
            //If we find that the current binary sort tree has only one node
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            //To find the parent node of targetNode
            Node1 parent = searchParent(id);
            //If the node to be deleted is a leaf node
            if (targetNode.left == null && targetNode.right == null) {
                //Judge whether the targetNode is the left child node or the right child node of the parent node
                if (parent.left != null && parent.left.id == id) {
                    //Is the left child node
                    parent.left = null;
                } else if (targetNode.left != null && parent.right.id == id) {
                    //Is the right child node
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) {//Delete a node with two subtrees
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.id = minVal;
            } else {
                //Delete nodes with only one subtree
                if (targetNode.left != null) {
                    if (parent != null) {
                        //If the targetNode is the left child node of the parent
                        if (parent.left.id == id) {
                            parent.left = targetNode.left;
                        } else {//targetNode is the right child node of the parent
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else {
                    //If the node to be deleted has a right child node
                    if (parent != null) {
                        //If the targetNode is the left child node of the parent
                        if (parent.left.id == id) {
                            parent.left = targetNode.right;
                        } else {
                            //If the targetNode is the right child node of the parent
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }
            }
        }
    }

    /**
     * Adding nodes to a sorted binary tree
     *
     * @param node1 Node to add
     */
    public void add(Node1 node1) {
        if (root == null) {
            root = node1;
        } else {
            this.root.addNode(node1);
        }
    }


}

class Node1 {
    int id;
    Node1 left;
    Node1 right;

    public Node1(int id) {
        this.id = id;
    }

    /**
     * Medium order traversal
     */
    public void midOrder() {
        if (this.left != null) {
            this.left.midOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.midOrder();
        }
    }

    /**
     * Add node
     *
     * @param node1 Node to add
     */
    public void addNode(Node1 node1) {
        if (node1 == null) {
            return;
        }
        if (node1.id < this.id) {//If the id value of the incoming node is smaller than the id value of the node that is currently adjusting the method,
            if (this.left == null) {
                this.left = node1;
            } else {//If it is not empty, it is added recursively
                this.left.addNode(node1);
            }
        } else {//If the id value of the incoming node is larger or equal to the id value of the node that is currently adjusting the method.
            if (this.right == null) {
                this.right = node1;
            } else {
                //Recursive addition
                this.right.addNode(node1);
            }
        }
    }

    /**
     * Find the node to delete
     *
     * @param id Weights of nodes to be deleted
     * @return If found, the node is returned; otherwise, null is returned
     */
    public Node1 search(int id) {
        if (id == this.id) {//This node is found
            return this;
        } else if (id < this.id) {//If the search value is less than the current node, the left subtree will be searched recursively
            if (this.left == null) {//If the left child node is empty
                return null;
            }
            //Otherwise, left recursive lookup
            return this.left.search(id);
        } else {
            //If the search value is not less than the current node, recursively search the right subtree
            if (this.right == null) {
                return null;
            }
            //Otherwise, recursive search to the right
            return this.right.search(id);
        }
    }

    /**
     * Find the parent node of the node to delete
     *
     * @param id The weight of the node to be found
     * @return If found, the node is returned; otherwise, null is returned
     */
    public Node1 searchParent(int id) {
        //If the current node is the parent node of the node to be deleted, return
        if ((this.left != null && this.left.id == id) ||
                (this.right != null && this.right.id == id)) {
            return this;
        } else {
            //If the searched value is less than the value of the current node, and the left child node of the current node is not empty
            if (id < this.id && this.left != null) {
                return this.left.searchParent(id);//Left subtree recursive lookup
            } else if (id >= this.id && this.right != null) {
                return this.right.searchParent(id);//Recursive search of right subtree
            } else {
                return null;//Parent node not found
            }
        }
    }

    /**
     * Delete Vertex 
     *
     * @param id Weights of nodes to be deleted
     */
    public void deleteNode(int id) {


    }

    @Override
    public String toString() {
        return "Node1{" +
                "id=" + id +
                '}';
    }
}

Balanced binary tree (AVL)

Balanced binary tree (AVL): balanced binary tree appears to solve some small problems of sorting binary tree. For example, if we form {1, 2, 3, 4, 5} into a sorting binary tree, the sorting binary tree is like a single linked list; In this case, although the addition speed has no impact, the query speed is significantly reduced, and the advantages of sorted binary tree (BST) can not be brought into play. Even the query speed is slower than that of single linked list. Therefore, this provides a scheme - balanced binary tree.

Balanced binary tree is also called self balancing binary search tree, also known as AVL tree, which can ensure high query efficiency. It has the following characteristics: it is an empty tree or the absolute value of the height difference between its left and right subtrees does not exceed 1, and both the left and right subtrees are a balanced binary tree. The common implementation methods of balanced binary tree include red black tree, AVL, scapegoat tree, tree, extension tree, etc.

For example, which trees are balanced binary trees? Why?

The height difference between the left and right subtrees of the first two trees shall not exceed 1. The height difference between the left and right subtrees of the third tree is 3 - 1 = 2 > 1, so it does not meet the definition of balanced binary tree.

Here, I will use Mr. Han's diagram to explain how to realize it, because Mr. Han's diagram is very classic.

We need to adjust the sequential binary tree to a balanced binary tree.

The following figure shows that the height of the right subtree is greater than that of the left subtree:

Left rotation operation:

  What should be done when the height of the left subtree is greater than the height of the right subtree?

At this time, we need to rotate the tree right:

Another situation may occur at this time. Take right rotation as an example. When the height of the left subtree is greater than the height of the right subtree, we need to rotate left. However, if the height of the right subtree of the left subtree of the node is greater than the height of the left subtree, we will find that the binary tree obtained by directly rotating left on the node is still not a balanced binary tree, The reason is that the height of the right subtree of the left subtree of the node is greater than that of the left subtree. In order to solve this problem, we need to rotate the left subtree of the node first, and then rotate the node right. At this time, we will find that a balanced binary tree has been formed.  

  That is, first rotate the 7 node to the left, and then rotate the whole tree to the right. You can get a balanced binary tree.  

Implementation code attached:

package com.liu.treealgorithm;


/**
 * @author liuweixin
 * @create 2021-09-19 10:15
 */
//AVL tree, namely balanced binary tree
public class AVLTree {
    Node2 root;

    public static void main(String[] args) {
        int[] arr = new int[]{10, 11, 7, 6, 8, 9};
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node2(arr[i]));
        }
//        System.out.println("left turn forward");
//        System.out.println("tree height is:" + avlTree.root.height());
//        System.out.println("height of left subtree:" + avlTree.root.leftHeight());
//        System.out.println("right subtree height:" + avlTree.root.rightHeight());
//        System.out.println("the root node of the tree is:" + AVLTree. Root ");
//        System.out.println("after left turn");
//        avlTree.root.leftRotate();
        //The balance operation is realized when the node is inserted
        System.out.println("The height of the tree is:" + avlTree.root.height());
        System.out.println("The height of the left subtree is:" + avlTree.root.leftHeight());
        System.out.println("The height of the right subtree is:" + avlTree.root.rightHeight());
        System.out.println("The root node of the tree is:" + avlTree.root);
        //Preorder arrangement
        avlTree.preOrder();
    }

    public void preOrder() {
        if (root == null) {
            return;
        }
        this.root.preOrder();
    }

    /**
     * Adding nodes to a sorted binary tree
     *
     * @param node1 Node to add
     */
    public void add(Node2 node1) {
        if (root == null) {
            root = node1;
        } else {
            this.root.addNode(node1);
            if (this.root.rightHeight() - this.root.leftHeight() > 1) {
                //If the difference between the height of the right subtree and the left subtree of the current tree is greater than 1, the left rotation is required
                //If the height of the left subtree of its right subtree is greater than the height of the right subtree of its right subtree
                if (this.root.right != null && this.root.right.leftHeight() > this.root.right.rightHeight()) {
                    //First rotate the right child node to the right, and then rotate the current node to the left
                    this.root.right.rightRotate();
                    this.root.leftRotate();
                } else {
                    this.root.leftRotate();
                }
                return;
            }
            if (this.root.leftHeight() - this.root.rightHeight() > 1) {
                //If the difference between the height of the left subtree and the right subtree of the current tree is greater than 1, the right rotation is required
                //If the height of the left subtree of its right subtree is greater than the height of the right subtree of its right subtree
                if (this.root.left != null && this.root.left.rightHeight() > this.root.left.leftHeight()) {
                    //First rotate the left child node to the left, and then rotate the current node to the right
                    this.root.left.leftRotate();
                    this.root.rightRotate();
                } else {
                    this.root.rightRotate();
                }
                return;
            }
        }
    }


    static class Node2 {
        int id;
        Node2 left;
        Node2 right;

        public Node2(int id) {
            this.id = id;
        }

        public void preOrder() {
            System.out.println(this);
            if (this.left != null) {
                this.left.preOrder();
            }
            if (this.right != null) {
                this.right.preOrder();
            }
        }

        //Returns the height of the left subtree
        public int leftHeight() {
            if (left == null) {
                return 0;
            }
            return left.height();
        }

        //Returns the height of the right subtree
        public int rightHeight() {
            if (right == null) {
                return 0;
            }
            return right.height();
        }

        //Returns the height of the number of with this node as the root node
        public int height() {
            //Recursion is used here. The reason for + 1 is to add the number of layers of the node
            return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
        }

        //Handle left rotation
        public void leftRotate() {
            //Create a new node first
            Node2 newNode2 = new Node2(this.id);
            //Point the left subtree of the new node to the left subtree of the current node
            newNode2.left = this.left;
            //Point the right subtree of the new node to the left subtree of the right subtree of the current node
            newNode2.right = this.right.left;
            //Change the value of the current node to the value of the right subtree of the current node
            this.id = this.right.id;
            //The left subtree of the current node points to the new node, and the right subtree points to the right subtree of the right subtree
            this.left = newNode2;
            this.right = this.right.right;
        }

        //Handle right rotation
        public void rightRotate() {
            //In fact, right rotation is the same as left rotation
            Node2 newNode = new Node2(this.id);
            newNode.right = this.right;
            newNode.left = this.left.right;
            this.id = this.left.id;
            this.left = this.left.left;
            this.right = newNode;
        }

        /**
         * Add node
         *
         * @param node1 Node to add
         */
        public void addNode(Node2 node1) {
            if (node1 == null) {
                return;
            }
            if (node1.id < this.id) {//If the id value of the incoming node is smaller than the id value of the node that is currently adjusting the method,
                if (this.left == null) {
                    this.left = node1;
                } else {//If it is not empty, it is added recursively
                    this.left.addNode(node1);
                }
            } else {//If the id value of the incoming node is larger or equal to the id value of the node that is currently adjusting the method.
                if (this.right == null) {
                    this.right = node1;
                } else {
                    //Recursive addition
                    this.right.addNode(node1);
                }
            }
        }

        @Override
        public String toString() {
            return "Node2{" +
                    "id=" + id +
                    '}';
        }
    }
}

summary

    The knowledge of this part of the tree is more difficult than the array and linked list in front, so we need to knock and practice hard. I also slowly make my understanding more in-depth when reviewing. The above is basically my own understanding, which is for your reference. I hope you can gain something.

Tags: Java Algorithm data structure

Posted on Mon, 20 Sep 2021 21:38:25 -0400 by vbzoom.com