Tree of data structures and algorithms

Binary tree

Why do you need a tree data structure

Analysis of array storage mode
  • Advantages: it is fast to access elements by subscript. For ordered arrays, binary search can also be used to improve the retrieval speed.
  • Disadvantages: if you want to insert values (in a certain order), they will move as a whole, which is inefficient.
Analysis of chain storage mode
  • Advantages: the array storage method is optimized to a certain extent (for example, if you insert a numeric node, you only need to link the inserted node to the linked list, and the deletion efficiency is also very good).
  • Disadvantages: when retrieving, the efficiency is still low (for example, to retrieve a value, you need to traverse from the beginning of the node).
Tree storage mode analysis

It can improve the efficiency of data storage and reading. For example, using binary sort tree can not only ensure the speed of data retrieval, but also ensure the speed of data insertion, deletion and modification.

Concept of binary tree

  1. There are many kinds of trees. A tree with at most two child nodes per node is called a binary tree.
  2. The child nodes of binary tree are divided into left child node and right child node.
  3. If all leaf nodes of a binary tree are in the last layer, and the total number of nodes is 2n-1, n is the number of layers, we call it a full binary tree.
  4. 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 penultimate leaf nodes are continuous on the right, we call it a complete binary tree.
Binary tree traversal
  1. Preorder traversal (front root traversal): traverse the root first, then the left subtree, and then the right subtree.
  2. Middle order traversal (middle root traversal): first traverse the left subtree, then traverse the root, and finally traverse the right subtree.
  3. Subsequent traversal (post root traversal): first traverse the left subtree, then traverse the right subtree, and finally traverse the root.

Code implementation:

public class BinaryTreeDemo {

    public static void main(String[] args) {
        HeroNode root = new HeroNode(1, "Song Jiang");
        HeroNode node2 = new HeroNode(2, "Wu Yong");
        HeroNode node3 = new HeroNode(3, "Lu Junyi");
        HeroNode node4 = new HeroNode(4, "Lin Chong");

        BinaryTree binaryTree = new BinaryTree();
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        binaryTree.preOrder();
    }
}


class BinaryTree {

    private HeroNode root;

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

    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("Binary tree is empty and cannot be traversed");
        }
    }

    public void midOrder() {
        if (this.root != null) {
            this.root.midOrder();
        } else {
            System.out.println("Binary tree is empty and cannot be traversed");
        }
    }

    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("Binary tree is empty and cannot be traversed");
        }
    }
}


class HeroNode {

    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    // Omit setter and getter methods

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

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

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

    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }
        if (this.right != null) {
            this.right.postOrder();
        }
        System.out.println(this);
    }
}
Search of binary tree
  1. Preorder search
  2. Middle order search
  3. Sequential search

Code implementation:

package com.feng.tree;

public class BinaryTreeDemo {

    public static void main(String[] args) {
        HeroNode root = new HeroNode(1, "Song Jiang");
        HeroNode node2 = new HeroNode(2, "Wu Yong");
        HeroNode node3 = new HeroNode(3, "Lu Junyi");
        HeroNode node4 = new HeroNode(4, "Lin Chong");

        BinaryTree binaryTree = new BinaryTree();
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        HeroNode heroNode = binaryTree.preOrderSearch(3);
        System.out.println("Preorder search:" + heroNode);
    }
}


class BinaryTree {

    private HeroNode root;

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

    // Preorder search
    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            return root.preOrderSearch(no);
        }
        return null;
    }

    // Middle order search
    public HeroNode midOrderSearch(int no) {
        if (root != null) {
            return root.midOrderSearch(no);
        }
        return null;
    }

    // Sequential search
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return root.postOrderSearch(no);
        }
        return null;
    }
}


class HeroNode {

    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    // Omit setter and getter methods

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

    public HeroNode preOrderSearch(int no) {
        if (this.no == no) {
            return this;
        }
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
        if(resNode!=null) {
            return resNode;
        }
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    public HeroNode midOrderSearch(int no) {

        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.midOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        if (this.no == no) {
            return this;
        }
        if (this.right != null) {
            resNode = this.right.midOrderSearch(no);
        }
        return resNode ;
    }

    public HeroNode postOrderSearch(int no) {

        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.midOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        if (this.right != null) {
            resNode = this.right.midOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        if (this.no == no) {
            return this;
        }
        return resNode;
    }
}

Binary tree node deletion
  • appointment
  1. If a leaf node is deleted, the node is deleted.
  2. If a non leaf node is deleted, the subtree is deleted.

Code implementation:

public class BinaryTreeDemo {

    public static void main(String[] args) {
        HeroNode root = new HeroNode(1, "Song Jiang");
        HeroNode node2 = new HeroNode(2, "Wu Yong");
        HeroNode node3 = new HeroNode(3, "Lu Junyi");
        HeroNode node4 = new HeroNode(4, "Lin Chong");

        BinaryTree binaryTree = new BinaryTree();
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        binaryTree.delNode(3);
        System.out.println("Delete the tree after node 3");
        binaryTree.preOrder();
    }
}


class BinaryTree {

    private HeroNode root;

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

    public void delNode(int no) {
        if (root != null) {
            if (root.getNo() == no) {
                this.root = null;  // If the root node is the node to be deleted, delete it directly
            } else {
                this.root.delNode(no);
            }
        } else {
            System.out.println("The current tree is empty");
        }
    }
    
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("Binary tree is empty and cannot be traversed");
        }
    }
}


class HeroNode {

    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
	// Omit getter setter method
 	// Omit the toString method
    public void delNode(int no) {

        if (this.left != null && this.left.no == no) { // If the left child of the current node happens to be the node to be deleted
            this.left = null;
            return;
        }
        if (this.right != null && this.right.no == no) {// If the right child node of the current node is exactly the node to be deleted
            this.right = null;
            return;
        }

        if (this.left != null) { // If the left child node is not empty, search recursively
            this.left.delNode(no);
        }
        if (this.right != null) { //If the right child node is not empty, search recursively
            this.right.delNode(no);
        }
    }
}
Sequential storage binary tree (used in heap sorting)
  • Concept: from the perspective of data storage, data storage mode and tree storage mode can be converted to each other, that is, array can be converted into tree and tree can also be converted into array. As shown in the figure:
  • characteristic
    1. Sequential binary trees usually only consider complete binary trees.
    2. The left child node of the nth element is 2*n+1
    3. The right child node of the nth element is 2*n+2
    4. The parent node of the nth element is (n-1)/2
  • code implementation
    /**
     * Binary trees are stored as arrays
     * Traverses the array in the order before the binary tree
     */ 
    public class ArrBinaryTreeDemo {
    
        public static void main(String[] args) {
    
            int[] arr = {1, 2, 3, 4, 5, 6, 7};
            ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
            arrBinaryTree.preOrder(0);
            System.out.println();
        }
    }
    
    class ArrBinaryTree {
        private int[] arr;
    
        public ArrBinaryTree(int[] arr) {
            this.arr = arr;
        }
        // Subscript of array
        public void preOrder(int index) {
            if (arr == null || arr.length == 0) {
                System.out.println("The array is empty and cannot be traversed");
                return;
            }
            System.out.printf("%d ", arr[index]);
    
            if (index * 2 + 1 < arr.length) {
                preOrder(index * 2 + 1);
            }
            if (index * 2 + 2 < arr.length) {
                preOrder(index * 2 + 2);
            }
        }
    }
    
Cued binary tree
  • Basic introduction
  1. The binary linked list of N nodes contains n+1 empty finger needle fields [left and right of 4, left and right of 5, left and right of 6 and right of 7]. The null pointer field in the binary 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 clue binary tree. According to different cue properties, cue binary tree can be divided into pre ordered cue binary tree, middle ordered cue binary tree and post ordered cue binary tree.
  3. The previous node of a node is called the precursor node.
  4. The next node of a node is called the successor node.
  • Binary tree, order traversal, after cueing

After the clue binary tree, the properties left and right of the Node node are as follows:

  1. Left points to the left subtree, but it can also point to the precursor node.
  2. Right points to the right subtree or to the successor node.
  • Code implementation:
    public class ThreadedBinaryTreeDemo {
    
        public static void main(String[] args) {
            HeroNode root = new HeroNode(1, "jack");
            HeroNode node2 = new HeroNode(3, "tom");
            HeroNode node3 = new HeroNode(6, "lusi");
            HeroNode node4 = new HeroNode(8, "tom");
            HeroNode node5 = new HeroNode(10, "kity");
            HeroNode node6 = new HeroNode(14, "mask");
    
            root.setLeft(node2);
            root.setRight(node3);
            node2.setLeft(node4);
            node2.setRight(node5);
            node3.setLeft(node6);
    
            ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
            threadedBinaryTree.setRoot(root);
            threadedBinaryTree.threadedNodes();
    
            // Test with node5 and check the values of its left and right nodes
            HeroNode leftNode = node5.getLeft();
            HeroNode rightNode = node5.getRight();
    
            System.out.println(leftNode);
            System.out.println(rightNode);
            System.out.println("Traversing the threaded binary tree");
            threadedBinaryTree.threadedList();
        }
    }
    
    class ThreadedBinaryTree {
    
        private HeroNode root;
        private HeroNode pre;
    
        public void setRoot(HeroNode root) {
            this.root = root;
        }
    
        public void threadedNodes() {
            this.threadedNodes(root);
        }
    
        // Traversal cued binary tree
        public void threadedList() {
    
            HeroNode node = root;
            while (node != null) {
                // Keep looking until you find a node of type 1
                while (node.getLeftType() == 0) {
                    node = node.getLeft();
                }
                System.out.println(node);
                while (node.getRightType() == 1) {
                    node = node.getRight();
                    System.out.println(node);
                }
                node = node.getRight();
            }
        }
    
        public void threadedNodes(HeroNode node) {
            if (node == null) {
                return;
            }
            threadedNodes(node.getLeft());
            // Process the left child node of the current node
            if (node.getLeft() == null) {
                node.setLeft(pre);
                node.setLeftType(1);
            }
            // Process the right child node of the current node
            if(pre != null && pre.getRight() == null) {
                // Let the right pointer of the predecessor node point to the current node
                pre.setRight(node);
                pre.setRightType(1);
            }
            // After each node is processed, the current node is the precursor node of the next node
            pre = node;
            threadedNodes(node.getRight());
        }
    }
    
    class HeroNode {
    
        private int no;
        private String name;
        private HeroNode left;
        private HeroNode right;
        // If leftType == 0, it refers to the left subtree; if 1, it refers to the precursor node
        // If rightType == 0, it indicates that it points to the right subtree; if 1, it indicates that it points to the successor node
        private int leftType;
        private int rightType;
    
        public HeroNode(int no, String name) {
            this.no = no;
            this.name = name;
        }
    
        // Omit getter and setter methods
    
        @Override
        public String toString() {
            return "HeroNode{" +
                    "no=" + no +
                    ", name='" + name + '\'' +
                    ", leftType=" + leftType +
                    ", rightType=" + rightType +
                    '}';
        }
    }
    
huffman tree
  • Brief description
  1. Given n weights as n leaf nodes, a binary tree is constructed. If the weighted path length of the tree reaches the minimum, such a binary tree is called the optimal binary tree, also known as Huffman tree or Huffman tree.
  2. Huffman tree is the tree with the shortest weighted path length, and the node with large weight is very close to the root.
  • Important concepts
  1. Path: in a tree, the path between children or grandchildren that can be reached from one node down is called path.
  2. Path length: the number of branches in the path is called path length. If the specified number of layers of the root node is 1, the path length from the root node to the L-th layer node is L-1.
  3. Node weight: if a node in the book is assigned to a value with a certain meaning, this value is called the weight of the node.
  4. Weighted path length of a node: the product of the path length from the root node to the node and the weight of the node.
  5. Weighted path length of the tree: the weighted path length of the tree is specified as the sum of the weighted path lengths of all leaf nodes, which is recorded as WPL(Weighted Path Length). The binary tree with greater weight and closer to the root node is the optimal binary tree.
  6. The smallest WPL is Huffman tree.
  • Idea of creating Huffman tree
    Given a sequence {13, 7, 8, 3, 29, 6, 1}, it is required to be transformed into a Huffman tree.
  1. Sort from small to large, and regard each data (each data is a node) as the simplest binary tree.
  2. Take the two binary trees with the smallest weight at the root node.
  3. A new binary tree is formed, and the weight of the root node of the new binary tree is the sum of the weight of the root node of the previous two binary trees.
  4. Then the new binary tree is sorted again according to the weight of the root node, and the steps 1 ~ 4 are repeated continuously. Until all the data in the sequence is processed, a Huffman tree is obtained.
  • Code implementation:
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class HuffmanTreeDemo {
    
        public static void main(String[] args) {
            int[] arr = {13, 7, 8, 3, 29, 6, 1};
            Node node = createHuffmanTree(arr);
            preOrder(node);
        }
    
        public static void preOrder(Node root) {
            if (root != null) {
                root.preOrder();
            } else {
                System.out.println("Huffman tree is empty");
            }
        }
    
        // Method of creating Huffman tree
        public static Node createHuffmanTree(int[] arr) {
    
            List<Node> nodes = new ArrayList<>();
            for (int value : arr) {
                nodes.add(new Node(value));
            }
    
            while (nodes.size() > 1) {
    
                Collections.sort(nodes);
                Node leftNode = nodes.get(0);
                Node rightNode = nodes.get(1);
    
                Node parentNode = new Node(leftNode.value + rightNode.value);
                parentNode.left = leftNode;
                parentNode.right = rightNode;
    
                nodes.remove(leftNode);
                nodes.remove(rightNode);
    
                nodes.add(parentNode);
            }
            return nodes.get(0);
        }
    }
    
    // In order to make Node objects sort, you need to implement the Comparable interface
    class Node implements Comparable<Node>{
        int value;  // Node weight
        Node left;  // Point to left child node
        Node right; // Point to the right child node
    
        public Node(int value) {
            this.value = value;
        }
    
        public void preOrder() {
            System.out.println(this);
            if (this.left != null) {
                this.left.preOrder();
            }
            if (this.right != null) {
                this.right.preOrder();
            }
        }
    
        @Override
        public int compareTo(Node o) {
            return this.value - o.value;
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    }
    

Tags: Algorithm data structure Binary tree

Posted on Wed, 01 Sep 2021 14:44:42 -0400 by sethadam1