[java]-Algorithms and Data Structures-Chapter Ten-Tree Structure

Ten, tree structure

1. Basic part

1) lead out

  1. Analysis of array storage method
    • Advantages: Accessing elements through subscripts is fast. For ordered arrays, binary search can also be used to improve retrieval speed.
    • Disadvantages: If you want to retrieve a specific value, or insert values ​​(in a certain order) will move as a whole, the efficiency is low

insert image description here

  1. Analysis of Chained Storage Mode

    • Advantages: To a certain extent, the array storage method is optimized (for example: to insert a value node, you only need to link the inserted node to the linked list, and the deletion efficiency is also very good).
    • Disadvantages: When searching, the efficiency is still low, such as (to retrieve a certain value, you need to traverse from the head node)
      insert the picture description here
  2. The analysis of the tree storage method
    can improve the efficiency of data storage and reading. For example, using a binary sort tree (Binary Sort Tree) can not only ensure the speed of data retrieval, but also ensure the speed of data insertion, deletion, and modification.

insert image description here

2) Common terms

Tree

  1. node
  2. root node
  3. parent node
  4. group node
  5. leaf nodes (nodes with no children)
  6. Node's weight (the value of the node)
  7. path (route to find this node from the root node)
  8. layer
  9. subtree
  10. The height of the tree (maximum number of layers and)
  11. Forest: Multiple subtrees form a forest

insert image description here

2. Binary tree

1) Concept

  1. There are many kinds of trees, and each node can only have at most two child nodes. One form is called a binary tree

  2. The child nodes of a binary tree are divided into left nodes and right nodes.
    insert image description here

  3. If all the 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, then we call it a full binary tree.

  4. If all the leaf nodes of the binary tree are in the last layer or the second-to-last layer, and the leaf nodes of the last layer are continuous on the left, and the leaf nodes of the penultimate layer are continuous on the right, we call it a complete binary tree.
    insert image description here

2) traverse

illustrate:

  1. Preorder traversal: output the parent node first, then traverse the left and right subtrees
  2. In-order traversal: first traverse the left subtree, then output the parent node, and then traverse the right subtree
  3. Post-order traversal: first traverse the left subtree, then traverse the right subtree, and finally output the parent node

Summary: Look at the order of the output parent nodes to determine whether it is pre-order, in-order or post-order

Traverse steps:

  1. create binary tree
  2. preorder traversal
    • First output the current node (initially the root node)
    • If the left child node is not empty, recursively continue the preorder traversal
    • If the right child node is not empty, recursively continue the preorder traversal
  3. Inorder traversal
    • If the left child of the current node is not empty, recursive inorder traversal
    • output the current node
    • If the right child of the current node is not empty, recursive inorder traversal
  4. post order traversal
    • If the left child node of the current node is not empty, recursive post-order traversal
    • If the right child node of the current node is not empty, recursive post-order traversal
    • output the current node

code :

public class 二叉树 {
    
    
    public static void main(String[] args) {
    
    

        BinaryTree binaryTree = new BinaryTree();
        HeroNode node1 = new HeroNode(1, "A");
        HeroNode node2 = new HeroNode(2, "B");
        HeroNode node3 = new HeroNode(3, "C");
        HeroNode node4 = new HeroNode(4, "D");
        HeroNode node5 = new HeroNode(5, "E");
        binaryTree.setRoot(node1);
        HeroNode root = binaryTree.getRoot();
        root.setLeft(node2);
        root.setRight(node3);
        node3.setLeft(node5);
        node3.setRight(node4);
        System.out.println("---------前序遍历---------");
        binaryTree.preOrder();  // 1,2,3,4
        System.out.println("---------中序遍历---------");
        binaryTree.infixOrder(); // 2,1,3,4
        System.out.println("---------后序遍历---------");
        binaryTree.postOrder(); // 2,4,3,1

    }
}

  • BinaryTree
// 二叉树
class BinaryTree {
    
    
    private HeroNode root;

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

    public HeroNode getRoot() {
    
    
        return root;
    }

    // 前序遍历
    public void preOrder() {
    
    
        if (this.root != null) {
    
    
            this.root.preOrder();
        } else {
    
    
            System.out.println("二叉树为空,无法遍历");
        }
    }

    public void infixOrder() {
    
    
        if (this.root != null) {
    
    
            this.root.infixOrder();
        } else {
    
    
            System.out.println("二叉树为空,无法遍历");
        }
    }

    public void postOrder() {
    
    
        if (this.root != null) {
    
    
            this.root.postOrder();
        } else {
    
    
            System.out.println("二叉树为空,无法遍历");
        }
    }

}
  • HeroNode
// node
class HeroNode {
    
    
    private int NO;
    private String name;
    private HeroNode left;
    private HeroNode right;

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

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

    public int getNO() {
    
    
        return NO;
    }

    public void setNO(int NO) {
    
    
        this.NO = NO;
    }

    public String getName() {
    
    
        return name;
    }

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

    public HeroNode getLeft() {
    
    
        return left;
    }

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

    public HeroNode getRight() {
    
    
        return right;
    }

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

    // 前序遍历
    public void preOrder() {
    
    
        // 1. 先输出父节点
        System.out.println(this);
        // 2. 递归左子树
        if (this.left != null) {
    
    
            this.left.preOrder();
        }
        // 3. 递归右子树
        if (this.right != null) {
    
    
            this.right.preOrder();
        }
    }

    // 中序遍历
    public void infixOrder() {
    
    
        // 1. 递归左子树
        if (this.left != null) {
    
    
            this.left.infixOrder();
        }
        // 2. 输出父节点
        System.out.println(this);
        // 3. 向右子树遍历
        if (this.right != null) {
    
    
            this.right.infixOrder();
        }
    }


    // 后序遍历
    public void postOrder() {
    
    
        // 1. 递归左子树
        // 2. 向右子树遍历
        // 3. 输出父节点
        if (this.left != null) {
    
    
            this.left.postOrder();
        }
        if (this.right != null) {
    
    
            this.right.postOrder();
        }
        System.out.println(this);
    }
}

3) Find

  • the code

    // 前序查找
    public HeroNode preSearch(int value) {
    
    
        // 1. 比较当前节点是不是
        if (this.getNO() == value) {
    
    
            return this;
        }
        // 2. 判断左节点
        HeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.preSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 3. 判断右节点
        if (this.right != null) {
    
    
            resNode = this.right.preSearch(value);
        }
        return resNode;
    }

    // 中序查找
    public HeroNode infixSearch(int value) {
    
    

        HeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.infixSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 2. 判断当前节点
        if (value == this.NO) {
    
    
            return this;
        }
        // 3. 向右
        if (this.right != null) {
    
    
            resNode = this.right.infixSearch(value);
        }
        return resNode;
    }

    // 后序查找
    public HeroNode postSearch(int value) {
    
    

        // 1. 向左遍历
        HeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.postSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 2. 向右
        if (this.right != null) {
    
    
            resNode = this.right.postSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 3. 判断当前节点
        if (value == this.NO) {
    
    

            return this;
        }
        return null;
    }

4) delete

  1. Require

    If the deleted node is a leaf node, delete the node

    If the deleted node is a non-leaf node, the subtree is deleted.

    Test, delete leaf node No. 5 and subtree No. 3.

  • HeroNode
    // 删除
    public void delete(int NO) {
    
    
        // 1. 左节点
        if (this.left != null && this.left.getNO() == NO) {
    
    
            this.left = null;
            return;
        }
        // 2. 右节点
        if (this.right != null && this.right.getNO() == NO) {
    
    
            this.right = null;
            return;
        }
        // 3. 递归删除
        if(this.left != null){
    
    
            this.left.delete(NO);
        }
        if(this.right != null){
    
    
            this.right.delete(NO);
        }

    }
  • BinaryTree
    // 删除
    public void delete(int no){
    
    
        if(this.root == null){
    
    
            System.out.println("此树为空");
            return;
        }else {
    
    
            if(this.root.getNO() == no){
    
    
                this.root = null;
            }else {
    
    
                this.root.delete(no);
            }
        }
    }

3. Sequential storage of binary trees

1) Concept

  1. From the perspective of data storage, the array storage method and the tree storage method can be converted to each other, that is, the array can be converted into a tree, and the tree can also be converted into an array. See the diagram on the right.
    insert image description here

  2. features

    • Sequential binary trees usually only consider complete binary trees
    • The left child node of the nth element is 2*n +1;
    • The right child node of the nth element is 2*n +2;
    • The parent node of the nth element is (n-1)/2;

2) traverse

  • the code
public class 数组二叉树 {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    1, 2, 3, 4, 5, 6, 7};
        ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree(arr);
        arrayBinaryTree.preOrder();
    }
}

class ArrayBinaryTree {
    
    
    private int[] arr; // 存储数据节点的数组

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


    public void preOrder() {
    
    
        this.preOrder(0);
    }

    // 顺序存储
    // 前序  index 为数组下标
    public void preOrder(int index) {
    
    
        if (arr == null || arr.length == 0) {
    
    
            System.out.println("数组二叉树为空");
            return;
        }
        // 输出当前元素
        System.out.println(arr[index]);
        // 向左递归
        if ((index * 2 + 1) < arr.length) {
    
    
            preOrder(2 * index + 1);
        }
        // 向右边递归
        if ((index * 2 + 2) < arr.length) {
    
    
            preOrder(2 * index + 2);
        }
    }
}

4. Threaded Binary Tree

1) Concept

  1. The binary linked list of n nodes contains n+1 [formula 2n-(n-1)=n+1] null pointer fields. Use the null pointer field in the binary linked list to store pointers to the predecessor and successor nodes of the node in a certain traversal order (this additional pointer is called a "clue")
  2. This binary linked list with threads is called a threaded linked list, and the corresponding binary tree is called a threaded binary tree (Threaded BinaryTree). According to the different properties of the clues, the clue binary tree can be divided into three types: the pre-order clue binary tree, the in-order clue binary tree and the post-order clue binary tree.
  3. The previous node of a node is called the predecessor node
  4. A node after a node is called a successor node

2) Diagram

insert image description here

Explanation: After threading the binary tree, the attributes left and right of the Node node have the following conditions:

  1. left points to the left subtree, and may also be the predecessor node pointed to. For example, the left subtree pointed to by the node ileft, and the left of the ⑩ node points to the predecessor node.
  2. right points to the right subtree, and may also point to the successor node, for example, ① node right points to the right subtree, and ⑩ node right points to the successor node.

3) Realize

public class 线索二叉树 {
    
    
    public static void main(String[] args) {
    
    
        NewHeroNode node1 = new NewHeroNode(1, "A");
        NewHeroNode node2 = new NewHeroNode(3, "B");
        NewHeroNode node3 = new NewHeroNode(6, "C");
        NewHeroNode node4 = new NewHeroNode(8, "D");
        NewHeroNode node5 = new NewHeroNode(10, "E");
        NewHeroNode node6 = new NewHeroNode(14, "F");

        node1.left = node2;
        node1.right = node3;
        node2.left = node4;
        node2.right = node5;
        node3.left = node6;

        ThreeNodes threeNodes = new ThreeNodes();
        threeNodes.setRoot(node1);

        System.out.println(node5.getLeft());
        System.out.println(node5.getRight() );
        node1.infixOrder();


    }

}

// 实现线索化二叉树
class NewHeroNode {
    
    

    public int NO;
    public String name;
    public NewHeroNode left;
    public NewHeroNode right;
    public int leftType;
    public int rightType;


    public NewHeroNode(int NO, String name) {
    
    
        super();
        this.NO = NO;
        this.name = name;
    }

    // 前序遍历
    public void preOrder() {
    
    
        // 1. 先输出父节点
        System.out.println(this);
        // 2. 递归左子树
        if (this.left != null) {
    
    
            this.left.preOrder();
        }
        // 3. 递归右子树
        if (this.right != null) {
    
    
            this.right.preOrder();
        }
    }

    // 中序遍历
    public void infixOrder() {
    
    
        // 1. 递归左子树
        if (this.left != null) {
    
    
            this.left.infixOrder();
        }
        // 2. 输出父节点
        System.out.println(this);
        // 3. 向右子树遍历
        if (this.right != null) {
    
    
            this.right.infixOrder();
        }
    }

    // 后序遍历
    public void postOrder() {
    
    
        // 1. 递归左子树
        // 2. 向右子树遍历
        // 3. 输出父节点
        if (this.left != null) {
    
    
            this.left.postOrder();
        }
        if (this.right != null) {
    
    
            this.right.postOrder();
        }
        System.out.println(this);
    }

    // 前序查找
    public NewHeroNode preSearch(int value) {
    
    
        // 1. 比较当前节点是不是
        if (this.getNO() == value) {
    
    
            return this;
        }
        // 2. 判断左节点
        NewHeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.preSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 3. 判断右节点
        if (this.right != null) {
    
    
            resNode = this.right.preSearch(value);
        }
        return resNode;
    }

    // 中序查找
    public NewHeroNode infixSearch(int value) {
    
    

        NewHeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.infixSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 2. 判断当前节点
        if (value == this.NO) {
    
    
            return this;
        }
        // 3. 向右
        if (this.right != null) {
    
    
            resNode = this.right.infixSearch(value);
        }
        return resNode;
    }

    // 后序查找
    public NewHeroNode postSearch(int value) {
    
    

        // 1. 向左遍历
        NewHeroNode resNode = null;
        if (this.left != null) {
    
    
            resNode = this.left.postSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 2. 向右
        if (this.right != null) {
    
    
            resNode = this.right.postSearch(value);
        }
        if (resNode != null) {
    
    
            return resNode;
        }
        // 3. 判断当前节点
        if (value == this.NO) {
    
    

            return this;
        }
        return null;
    }

    // 删除
    public void delete(int NO) {
    
    
        // 1. 左节点
        if (this.left != null && this.left.getNO() == NO) {
    
    
            this.left = null;
            return;
        }
        // 2. 右节点
        if (this.right != null && this.right.getNO() == NO) {
    
    
            this.right = null;
            return;
        }
        // 3. 递归删除
        if (this.left != null) {
    
    
            this.left.delete(NO);
        }
        if (this.right != null) {
    
    
            this.right.delete(NO);
        }

    }
}

class ThreeNodes {
    
    
    public NewHeroNode pre = null;

    public NewHeroNode root = null;


    // node 为当前需要线索化的节点
    public void threadedNodes(NewHeroNode node) {
    
    
        if (node == null) {
    
    
            return;
        }
        // 1. 线索化左子树
        threadedNodes(node.getLeft());
        // 2. 线索化当前节点
        // 1) 先梳理当前节点的前驱节点
        if (node.getLeft() == null) {
    
    
            // 让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            // 修改当前节点的左指针的类型
            node.setLeftType(1);
        }
        // 2) 处理后置节点
        if (pre != null && pre.getRight() == null) {
    
    
            pre.setRight(node);
            pre.setRightType(1);
        }
        // 每次处理完一个节点,当前节点为下一个节点的前驱节点
        pre = node;

        // 3. 线索化右子树
        threadedNodes(node.getRight());
    }

    public NewHeroNode getRoot() {
    
    
        return root;
    }

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

4) traverse

  • the code
 // 遍历
    public void threadList() {
    
    
        // 定义一个变量存储当前节点
        NewHeroNode node = root;
        while (node != null) {
    
    
            // 循环找到 leftType == 1 的结点,前驱节点
            // 当 leftType == 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();

        }
    }

5. Heap sort

1) Concept

  1. Heap sorting is a sorting algorithm designed using the data structure of the heap. Heap sorting is a selection sorting. Its worst, best, and average time complexity are O(nlogn), and it is also an unstable sort.
  2. The heap is a complete binary tree with the following properties: the value of each node is greater than or equal to the value of its left and right child nodes, called the big top heap, note: there is no requirement for the value of the left child of the node and the value of the right child size relationship.
  3. The value of each node is less than or equal to the value of its left and right child nodes, called the small top heap
  4. Big Top Heap Example

insert image description here

insert image description here

Number the nodes in the heap by layer, and map them to the array as follows For
example: arr[1] ≥ arr[2 * 1 + 1] && arr[1] ≥ arr[2 * 1 + 2]===45 ≥ 20 && 45 ≥ 25

  1. Small pile example
    insert image description here

2) thought

  1. Build the sequence to be sorted into a big top heap
  2. At this point, the maximum value of the entire sequence is the root node at the top of the heap
  3. Exchange it with the element at the end, and the end is the maximum value
  4. Reconstruct the heap with the remaining n- 1 elements, and repeat the above steps.

3) Diagram

  1. Construct an initial heap, constructing a given unordered sequence into a large top heap,
    insert image description here

  2. At this time, start from the last non-leaf node (the leaf node naturally does not need to be adjusted, and the first non-leaf node arr.length/2 -1 = 5/2-1 = 1, which is the 6 nodes below), is adjusted from left to right and from top to bottom.
    insert image description here

  3. Find the third non-leaf node 4, since 9 is the largest, 1 exchanges positions
    a

  4. continue to adjust

insert image description here

  1. Exchange the top pair of elements with the last element to make the last element the largest, then continue to adjust, and then exchange the top element with the last element to get the second largest element, and repeat the cycle.

insert image description here

insert image description here

insert image description here

insert image description here

4) Code

   public static void main(String[] args) {
    
    
        int[] arr = {
    
    4, 6, 8, 5, 9};
        heapSort(arr);
    }

    // 堆排序的方法
    public static void heapSort(int arr[]) {
    
    
        /*adjustHeap(arr, 1, arr.length);
        System.out.println(Arrays.toString(arr));
        adjustHeap(arr, 0, arr.length);
        System.out.println(Arrays.toString(arr));*/

        // 1. 构建大 顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
    
    
            adjustHeap(arr, i, arr.length);
        }
        System.out.println(Arrays.toString(arr));
        // 2. 将堆顶元素与末尾元素进行交换, 将最大元素沉到数组末端
        int temp = 0;
        for (int j = arr.length - 1; j > 0; j--) {
    
    
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, j);
        }
        System.out.println(Arrays.toString(arr));
    }

    // 讲一个数组调整成一个大顶堆
    // 功能:完成将 以 i 对应的非叶子节点的数调整成大顶堆
    // 举例:arr = {4,6,8,5,9} i = 1,length = 5

    // arr 待调整的数组
    // i 非叶子结点在数组中的索引
    // length 对多少个元素进行调整
    public static void adjustHeap(int arr[], int i, int length) {
    
    
        int temp = arr[i]; // 取出当前元素 -->非叶子元素
        // 1. 开始调整
        // 左子节点, k 是 i的 左子节点
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
    
    
            // k+1 > length 不比了
            if (k + 1 < length && arr[k] < arr[k + 1]) {
    
     // 说明 左子节点 < 右子节点
                k++; // k指向 右子节点

            }
            // k此时为左结点 或者 右结点
            if (arr[k] > temp) {
    
     // 子节点 > 父节点
                arr[i] = arr[k]; // 父节点 变为 子节点
                i = k; // 非叶子结点 继续循环
            } else break;
        }
        // for循环结束后, 已经将 以 i为父节点的数的最大值,放在了最前面。
        arr[i] = temp;


    }

6. Huffman tree

1) Concept

  1. Given n weights as n leaf nodes, construct a binary tree. If the weighted path length (wpl) of the tree reaches the minimum, such a binary tree is called an optimal binary tree, also known as a Huffman tree (HuffmanTree). ), and some books are translated as Huffman trees.
  2. The Huffman tree is the tree with the shortest weighted path length, and the nodes with larger weights are closer to the root.

2) Important concepts

  1. Path and path length:

    In a tree, the path between children or grandchildren nodes that can be reached downwards from a node is called a path.

    The number of branches in a pathway is called the path length. If the number of layers of the root node is specified as 1, 第L层the length of the path from the root node to the node isL-1

  2. Node weight and weighted path length:

    If a node in the tree is assigned a value with a certain meaning, this value is called the weight of the node.

    The weighted path length of a node is: the product of the path length from the root node to the node and the weight of the node

    For example: 2 * 13 = 26;
    insert image description here

  3. The 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, denoted as, the WPL(weighted path length)binary tree with the node with the larger weight value closer to the root node is the optimal binary tree.

  4. The smallest WPL is the Huffman tree

    [External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-NI0iyoM5-1651491300719)(https://secure2.wostatic.cn/static/syc9KrE5s3SqVDH32disos/image.png)]

3) Diagram

  1. Sorting from small to large, each data, each data is a node, each node can be regarded as the simplest binary tree
  2. Take out two binary trees with the smallest root node weight
  3. Form a new binary tree, the weight of the root node of the new binary tree is the sum of the weights of the root nodes of the previous two binary trees
  4. Then match this new binary tree with the weight of the root node again, and repeat.

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-OYuaGSji-1651491300719)(https://secure2.wostatic.cn/static/kg3JJLnh9aEbBVkdrVUUok/image.png)]

4) Code

  • the code
public class 赫夫曼树 {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    13, 7, 8, 3, 29, 6, 1};
        Node root = createHeffmanTree(arr);
        preNode(root);
    }

    // 创建赫夫曼树
    public static Node createHeffmanTree(int[] arr) {
    
    
        // 1. 遍历 arr 数组
        // 2. 将每一个构建成node
        // 3. 将每一个放入 arrayList中
        ArrayList<Node> nodes = new ArrayList<>();
        for (int value : arr) {
    
    
            nodes.add(new Node(value));
        }
        // 4. 排序 小 --> 大
        // 循环处理
        while (nodes.size() > 1) {
    
    
            Collections.sort(nodes);

            // 5. 取出权值最小的两个二叉树
            Node left = nodes.get(0);
            Node right = nodes.get(1);

            // 6. 构建新的二叉树
            Node parent = new Node(left.value + right.value);
            parent.left = left;
            parent.right = right;

            // 7. 从 arrayList中删除处理过的二叉树
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
            System.out.println(nodes);
        }
        return nodes.get(0);
    }

    // 前序遍历
    public static void preNode(Node root){
    
    
        if(root == null){
    
    
            System.out.println("值为空");
        }else {
    
    
            root.preOrder();
        }
    }
}
// 创建节点类


class Node implements Comparable<Node> {
    
    
    int value; // 权
    Node left;  // 左子节点
    Node right; // 右子节点

    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 String toString() {
    
    
        return "Node{" +
                "value=" + value +
                '}';
    }

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

7. Huffman coding

1) Concept

  1. Huffman coding is also translated as Huffman coding (Huffman Coding), also known as Huffman coding, is a coding method and belongs to a program algorithm
  2. Huffman coding is one of the classic applications of the Huffman tree in telecommunications.
  3. Huffman coding is widely used for data file compression. Its compression rate is usually between 20% and 90%.
  4. Huffman codes are a type of variable word length coding (VLC). Huffman proposed a coding method in 1952, called optimal coding

2) Diagram

  1. i like like like java do you like a java

  2. d:1y:1 u:1 j:2 v:2 o:2l:4 k:4 e:4 i:5 a:5 :9 //The number corresponding to each character

  3. Construct a Huffman tree according to the number of occurrences of the above characters, and the number of times is used as the weight

    [External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-DSRiQoz7-1651491300720)(https://secure2.wostatic.cn/static/i92Yt13Bex8adYwZJEP5mb/image.png)]

  4. According to the Huffman tree, codes are specified for each character, the path to the left is 0, and the path to the right is 1.

    o:1000 u: 10010 d:100110 y: 100111 a : 110 k:1110 e: 1111 j:0000 v:0001 l:001 :01

  5. i like like like java do you like a javaAccording to the Huffman encoding above, the corresponding encoding of our string is (note the lossless compression we use here)
    101``01``001101111011110100110111101111010011011110111101000010000110011001111000011001111000100100100110111011011100100001100001110

  6. Length is: 133

    The original length is 359, compressed by 62.9%

    This encoding satisfies prefix encoding, that is, the encoding of characters cannot be the prefix of other character encodings, which will not cause ambiguity in matching. Huffman encoding is a lossless processing method.

  7. Notice:

    The Huffman tree may be different depending on the sorting method, so the corresponding Huffman tree encoding is also different, but the WPL is the same, and the generated lengths are the same, and they are all the smallest.

insert image description here

3) Code

  • Node class

class Node2 implements Comparable<Node2> {
    
    
    Byte data;
    int weight;
    Node2 left;
    Node2 right;

    public Node2(Byte data, int weight) {
    
    
        this.data = data;
        this.weight = weight;
    }

    // 前序遍历
    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(Node2 o) {
    
    
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
    
    
        return "Node2{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }
}
  1. create node
private static List<Node2> getNodes(byte[] bytes) {
    
    
        // 1. 创建一个 ArrayList
        ArrayList<Node2> nodes = new ArrayList<>();
        // 2. 存储灭一个byte出现的次数
        HashMap<Byte, Integer> map = new HashMap<>();
        for (byte by : bytes) {
    
    
            //说明map中没有这个字符,我们将其加入到map中
            //说明这个字符已经存在,我们让其进行总数加1
            map.merge(by, 1, Integer::sum);
        }
        System.out.println(map);

        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
    
    
            nodes.add(new Node2(entry.getKey(), entry.getValue()));
        }
        return nodes;

    }
  1. Create a Huffman tree
public static Node2 createHeffmanTree(List<Node2> nodes) {
    
    

        // 4. 排序 小 --> 大
        // 循环处理
        while (nodes.size() > 1) {
    
    
            Collections.sort(nodes);

            // 5. 取出权值最小的两个二叉树
            Node2 left = nodes.get(0);
            Node2 right = nodes.get(1);

            // 6. 构建新的二叉树
            Node2 parent = new Node2(null, left.weight + right.weight);
            parent.left = left;
            parent.right = right;

            // 7. 从 arrayList中删除处理过的二叉树
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);

        }
        return nodes.get(0);
    }
  1. Create Huffman codes
    public static Map<Byte, String> getCodes(Node2 node2) {
    
    
        getCodes(node2, "", stringBuilder);
        return huffmanCodes;
    }

    private static void getCodes(Node2 node, String code, StringBuilder stringBuilder) {
    
    
        StringBuilder st = new StringBuilder(stringBuilder);
        //将code加入st
        st.append(code);
        if (node != null) {
    
    
            if (node.data == null) {
    
    //非叶子节点,叶子节点的data域是存在值的
                //递归处理
                //向左递归
                getCodes(node.left, "0", st);
                //向右递归
                getCodes(node.right, "1", st);
            } else {
    
    
                //说明找到一个叶子节点
                //就表示找到某个叶子节点的最后
                huffmanCodes.put(node.data, st.toString());
            }
        }
    }
  1. Parsing Huffman codes, compression
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
    
    
        // 1. 将 bytes 转成 heffman编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        // 2. 遍历 bytes 数组
        for (byte b : bytes) {
    
    
            stringBuilder.append(huffmanCodes.get(b));
        }
        int len = (stringBuilder.length() + 7) / 8;
        // 创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i += 8) {
    
     // 8位对应一个 byte
            String strByte;
            if (i + 8 > stringBuilder.length()) {
    
    
                // 不够 8 位
                strByte = stringBuilder.substring(i);
            } else strByte = stringBuilder.substring(i, i + 8);
            // 将 strByte 转成 一个byte,放入
            huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2);
            index++;
        }
        return huffmanCodeBytes;
    }
  1. decompress data
  private static byte[] deCode(Map<Byte, String> huffmanCodes, byte[] huffmanCodesBytes) {
    
    
        // 1. 将 huffmanCodesBytes  [-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
        //    重新转成字符串(huffman编码对应的二进制字符串)
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < huffmanCodesBytes.length; i++) {
    
    
            // 判断是不是最后一个字节
            boolean flag = (i == huffmanCodesBytes.length - 1);
            stringBuilder.append(byteToBitString(!flag, huffmanCodesBytes[i]));
        }
        // 3. 把字符串按照指定的赫夫曼编码进行解码
        HashMap<String, Byte> map = new HashMap<>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
    
    
            map.put(entry.getValue(), entry.getKey());
        }
        // 4. 存放 byte
        ArrayList<Byte> list = new ArrayList<>();
        for (int i = 0; i < stringBuilder.length(); ) {
    
    
            int count = 1;
            boolean flag = true;
            Byte b = null;
            while (flag) {
    
    
                String key = stringBuilder.substring(i, i + count);
                b = map.get(key);
                if (b != null) {
    
    
                    flag = false;

                } else {
    
    
                    count++;
                }
            }
            list.add(b);
            i += count;
        }
        byte[] bytes = new byte[list.size()];
        for (int i = 0; i < list.size(); i++) {
    
    
            bytes[i] = list.get(i);
        }
        return bytes;

    }
  1. Compressed file
private static void zipFile(String srcFile, String dstFile) throws IOException {
    
    
        FileInputStream inputStream = new FileInputStream(srcFile);
        byte[] bytes = new byte[inputStream.available()];
        // 读取文件
        inputStream.read(bytes);
        // 压缩完成
        byte[] huffmanZipBytes = huffmanZip(bytes);
        // 输出文件,存放压缩文件
        OutputStream outputStream = new FileOutputStream(dstFile);
        ObjectOutputStream oos = new ObjectOutputStream(outputStream);
        // 写入压缩数组
        oos.writeObject(huffmanZipBytes);
        // 写入赫夫曼编码
        oos.writeObject(huffmanCodes);
        // 写入完毕
        inputStream.close();
        oos.close();
        outputStream.close();
    }
  1. unzip files
private static void unzipFile(String zipFile, String dstFile) throws IOException, ClassNotFoundException {
    
    
        FileInputStream inputStream = new FileInputStream(zipFile);
        ObjectInputStream ooin = new ObjectInputStream(inputStream);
        byte[] bytes;
        // 读取文件
        bytes = (byte[]) ooin.readObject();
        Map<Byte, String> huffmanCodes = (Map<Byte, String>) ooin.readObject();
        // 解压
        byte[] deCode = deCode(huffmanCodes, bytes);
        // 输出文件,存放压缩文件
        OutputStream outputStream = new FileOutputStream(dstFile);
        // 写入压缩数组
        outputStream.write(deCode);
        // 写入完毕
        ooin.close();
        inputStream.close();
        outputStream.close();
    }
  1. public method
    // 5. 将前面的方法封装起来,便于我们的调用
    private static byte[] huffmanZip(byte[] bytes) {
    
    
        // 1. 合并
        List<Node2> nodes = getNodes(bytes);
        // 2. 生成赫夫曼树
        Node2 root = createHeffmanTree(nodes);
        // 3. 生成赫夫曼编码
        Map<Byte, String> codes = getCodes(root);
        // 4. 生成赫夫曼编码处理后的数据
        byte[] zipBytes = zip(bytes, huffmanCodes);
        return zipBytes;
    }
    
        // 将 byte 转成二进制的字符串
    private static String byteToBitString(boolean flag, byte b) {
    
    
        int temp = b;
        // 如果是正数,还存在补高位的问题
        if (flag) {
    
     // 标识是否需要高位
            temp |= 256; // temp 1 --> 000;
        }

        String string = Integer.toBinaryString(temp); // 返回的是 temp 对应的二进制的补码
        if (flag) {
    
    
            return string.substring(string.length() - 8);
        } else {
    
    
            return string;
        }

    }

    // 前缀遍历
    public static void preOrder(Node2 root) {
    
    
        root.preOrder();
    }

8. Sorting Binary Tree (BST)

1) Concept

Binary sorting tree: BST: (Binary Sort(Search) Tree), for any non-leaf node of the binary sorting tree, the value of the left child node is required to be smaller than the value of the current node, and the value of the right child node is smaller than the value of the current node The value is large.

Special Note: If they have the same value, the node can be placed in the left child node or the right child node.

For example, for the previous data (7,3,10,12,5,1,9), the corresponding binary sorting tree is:

insert image description here

2) Code

  1. Node
// Node 节点
class Node3 {
    
    
    int value;
    Node3 left;
    Node3 right;

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

    // 添加节点
    public void add(Node3 node) {
    
    
        if (node == null) return;
        if (this.value > node.value) {
    
    
            if (this.left == null) {
    
    
                this.left = node;
            } else {
    
    
                this.left.add(node);
            }
        } else {
    
    
            if (this.right == null) {
    
    
                this.right = node;
            } else {
    
    
                this.right.add(node);
            }
        }
    }

    // 中序遍历
    public void infixOrder() {
    
    
        if (this.left != null) {
    
    
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
    
    
            this.right.infixOrder();
        }
    }

    @Override
    public String toString() {
    
    
        return "Node3{" +
                "value=" + value +
                '}';
    }
}
  1. BinarySortTree
 // 二叉排序树
class BinarySortTree {
    
    
    private Node3 root;

    // 添加节点
    public void add(Node3 node) {
    
    
        if (root == null) {
    
    
            root = node;
        } else {
    
    
            root.add(node);
        }
    }

    // 遍历
    public void infixOrder() {
    
    
        if (root != null) {
    
    
            root.infixOrder();
        } else {
    
    
            System.out.println("当前二叉排序树为空");
        }
    }

}

3) delete

  • Delete leaf nodes (for example: 2, 5, 9, 12)

    1. You need to find the node targetNode to delete first
    2. Find the parent node parent of targetNode
    3. Determine whether targetNode is the left or right child of parent
    4. Corresponding deletion according to the previous situation

    Left child node parent.left = null

    Right child node parent.right = null;

  • Delete a node with only one subtree, such as 1

    1. You need to find the node targetNode to delete first
    2. Find the parent node parent of targetNode
    3. Determine whether the child of targetNode is a left child or a right child
    4. targetNode is the left or right child of the parent

    If targetNode has a left child

    • If targetNode is the left child of parent

      parent.left = targetNode.left;

    • If targetNode is the right child of parent

      parent.right = targetNode.left;

    If targetNode has a right child node

    • If targetNode is the left child of parent

      parent.left = targetNode.right;

    • If targetNode is the right child of parent

      parent.right = targetNode.right

  • Delete the node with two subtrees. (eg: 7, 3, 10 )

    1. Need to find the node targetNode to be deleted first
    2. Find the parent node parent of targetNode
    3. Find the smallest node from the right subtree of targetNode
    4. Use a temporary variable to save the value of the smallest node temp = 11
    5. delete the smallest node

    targetNode.value = temp

4) Delete the code

  1. node

    // 查找删除的节点
    public Node3 search(int value) {
    
    

        if (value < this.value) {
    
     // 小于当前节点  向左查
            if (this.left == null) {
    
    
                return null;
            }
            return this.left.search(value);
        } else if (value > this.value) {
    
    
            if (this.right == null) {
    
    
                return null;
            }
            return this.right.search(value);
        } else {
    
    
            return this;
        }

    }

    // 查找要删除节点的父节点
    public Node3 searchParent(int value) {
    
    
        // 当前节点就是要删除的节点的父节点,就返回
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
    
    
            return this;
        } else {
    
    
            // 查找的小于当前节点的值,并且当前节点的左子节点不为空
            if (value < this.value && this.left != null) {
    
     // 左递归
                return this.left.searchParent(value);
            } else if (value >= this.value && this.right != null) {
    
     // 右递归
                return this.right.searchParent(value);
            } else {
    
    
                return null;
            }
        }
    }
  1. BinarySortTree
    // 查找要删除的节点
    public Node3 search(int value) {
    
    
        if (root == null) return null;
        else return root.search(value);
    }

    // 查找父节点
    public Node3 searchParent(int value) {
    
    
        if (root == null) return null;
        else return root.searchParent(value);
    }

    // 删除节点
    public void delNode(int value) {
    
    
        if (root == null) return;
        else {
    
    
            // 1. 找节点
            Node3 targetNode = search(value);
            if (targetNode == null) return;
            // 2. 当前二叉排序树只有一个节点,删除当前节点
            if (root.left == null && root.right == null) {
    
    
                root = null;
                return;
            }

            // 3. 查找targetNode 的父节点
            Node3 parent = searchParent(value);
            // 4. 删除的节点是叶子节点
            if (targetNode.left == null && targetNode.right == null) {
    
    
                // 5. 判断是父节点的左子节点还是右
                if (parent.left != null && parent.left.value == value) {
    
     // 左子节点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) {
    
     // 右子节点
                    parent.right = null;
                }
            }
            // 6. 删除有两颗子树的节点
            else if (targetNode.left != null && targetNode.right != null) {
    
    
                int min = delRightThreeMin(targetNode.right);
                targetNode.value = min;

            }
            // 7. 删除只有一颗子树的节点
            else {
    
    

                // 如果要删除的节点有左子节点
                if (targetNode.left != null) {
    
    
                    if (parent == null) {
    
    
                        // 当前节点为根节点
                        root = targetNode.left;
                        return;
                    }
                    // targetNode 是 parent 的左子节点
                    if (parent.left.value == value) {
    
    
                        parent.left = targetNode.left;
                    } else {
    
     // targetNode 是parent 的右子节点
                        parent.right = targetNode.left;

                    }
                } else {
    
     // 要删除的节点有右子节点
                    if (parent.left.value == value) {
    
    
                        parent.left = targetNode.right;
                    } else {
    
    
                        // targetNode 是 parent 的右子节点
                        parent.right = targetNode.right;
                    }

                }
            }
        }
    }

    // 删除 node 为根节点的二叉排序树的最小节点
    public int delRightThreeMin(Node3 node) {
    
    
        // node 传入的节点(当作二叉排序树的根节点)
        // 返回的 以 node 为根节点的二叉排序树的最小节点的值
        Node3 targetNode = node;
        // 循环查找左节点, 就会找到最小值
        while (targetNode.left != null) {
    
    
            targetNode = targetNode.left;
        }
        // 这时 targetNode 就是最小值
        delNode(targetNode.value);
        return targetNode.value;

    }

9. Balanced Binary Tree (AVL)

1) Problems with BST

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-d5UTG4Ar-1652074825558)(https://secure2.wostatic.cn/static/hfvXXSAsLwTQXhvBJnjjej/image.png)]

  1. The left subtree is all empty, from the formal point of view, it is more like a singly linked list
  2. Insertion speed has no effect
  3. The query speed is obviously slower, and the advantages of BST cannot be used, because the left subtree needs to be compared every time, and its query speed is slower than that of the singly linked list

2) Concept

  1. A balanced binary tree is also called a balanced binary search tree (Self-balancing binary search tree), also known as an AVL tree, which can ensure high query efficiency.
  2. 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 left and right subtrees are a balanced binary tree. The common implementation methods of balanced binary tree are 红黑树, AVL, 替罪羊树, Treap, and 伸展树so on.
  3. For example. Look at the following AVL trees and why?
    insert image description here

3) Left rotation diagram

  1. Create a new node newNode (created with a value of 4), create a new node with a value equal to the value of the current root node

    Set the left subtree of the new node to the left subtree of the current node

  2. newNode.left = left

    Set the right subtree of the new node to the left subtree of the current node's right subtree

  3. newNode.right =right.left;

    Replace the value of the current node with the value of the right child node

  4. value=right.value;

    Set the right subtree of the current node to the right subtree of the right subtree

  5. right=right.right;

    Set the left subtree of the current node as the new node

  6. left=newleft;
    insert image description here

4) Left rotation code

  1. Node
// Node 节点
class Node4 {
    
    
    int value;
    Node4 left;
    Node4 right;

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

    // 添加节点
    public void add(Node4 node) {
    
    
        if (node == null) return;
        if (this.value > node.value) {
    
    
            if (this.left == null) {
    
    
                this.left = node;
            } else {
    
    
                this.left.add(node);
            }
        } else {
    
    
            if (this.right == null) {
    
    
                this.right = node;
            } else {
    
    
                this.right.add(node);
            }
        }

        // 当添加完一个节点后,如果条件满足,右子树的高度 > 左子树的高度 = 1; 左旋转
        if (rightHeight() - leftHeight() > 1) {
    
    
            /*if (right != null && right.rightHeight() < right.leftHeight()) {

            }*/
            leftRotate();
        }
    }

    // 中序遍历
    public void infixOrder() {
    
    
        if (this.left != null) {
    
    
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
    
    
            this.right.infixOrder();
        }
    }


    // 返回当前结点的高度,
    public int height() {
    
    
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    // 返回左子树的高度
    public int leftHeight() {
    
    
        if (left == null) {
    
    
            return 0;
        }
        return left.height();
    }

    // 返回右子树的高度
    public int rightHeight() {
    
    
        return right == null ? 0 : right.height();
    }

    // 左旋转方法
    public void leftRotate() {
    
    
        // 1. 创建新的结点
        Node4 newNode = new Node4(this.value);
        // 2. 把新的结点的左子树设置为当前节点的左子树
        newNode.left = this.left;
        // 3. 新的结点的右子树设置为过去节点的右子树的左子树
        newNode.right = this.right.left;
        // 4. 当前结点的值换成右子结点的值
        this.value = right.value;
        // 5. 把当前结点的右子树设置成右子树的右子树
        this.right = right.right;
        // 7. 当前节点的左子结点设置为新的结点
        this.left = newNode;
    }


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

5) Rotate diagram to the right

rotate right

It is to reduce the height of the left subtree. Here, the node 9 is rotated right to the right subtree

  1. Create a new node newNode (created with a value of 10), create a new node with a value equal to the value of the current root node

    Set the right subtree of the new node to the right subtree of the current node

  2. newNode.right =right

    Set the left subtree of the new node to the right subtree of the current node's eating subtree

  3. newNode.left=left.right;

    Replace the value of the current node with the value of the left child node

  4. valde=left.value,

    Set the left subtree of the current node as the eating subtree of the left subtree

  5. left=left.left;

    Set the right subtree of the current node as the new node

  6. right=newLeft;
    insert image description here

6) Right rotation code

Node


    // 右旋转
    public void rightRotate() {
    
    
        Node4 newNode = new Node4(this.value);
        newNode.right = this.right;
        newNode.left = this.left.right;
        this.value = this.left.value;
        this.left = this.left.left;
        this.right = newNode;
    }
    
    
    // 添加节点
    public void add(Node4 node) {
    
    
        if (node == null) return;
        if (this.value > node.value) {
    
    
            if (this.left == null) {
    
    
                this.left = node;
            } else {
    
    
                this.left.add(node);
            }
        } else {
    
    
            if (this.right == null) {
    
    
                this.right = node;
            } else {
    
    
                this.right.add(node);
            }
        }

        // 当添加完一个节点后,如果条件满足,右子树的高度 > 左子树的高度 = 1; 左旋转
        if (rightHeight() - leftHeight() > 1) {
    
    
            /*if (right != null && right.rightHeight() < right.leftHeight()) {

            }*/
//            leftRotate();
        }
        if (leftHeight() - rightHeight() > 1) {
    
    
            rightRotate();
        }
    }

7) Double rotation diagram

Problem graph:
insert image description here

solve

  1. Conditions when the symbol is rotated right
  2. If the height of the right subtree of its left subtree is greater than the height of its right subtree
  3. First rotate the left node of the current node to the left
  4. Then rotate the current node to the right

8) Double rotation code

Node

    // 添加节点
    public void add(Node4 node) {
    
    
        if (node == null) return;
        if (this.value > node.value) {
    
    
            if (this.left == null) {
    
    
                this.left = node;
            } else {
    
    
                this.left.add(node);
            }
        } else {
    
    
            if (this.right == null) {
    
    
                this.right = node;
            } else {
    
    
                this.right.add(node);
            }
        }

        // 当添加完一个节点后,如果条件满足,右子树的高度 > 左子树的高度 = 1; 左旋转
        if (rightHeight() - leftHeight() > 1) {
    
    
            if (right != null && right.leftHeight() > right.rightHeight()) {
    
    
                // 对右子节点进行右旋转

                right.rightRotate();
            }
            leftRotate();
            return;
        }
        if (leftHeight() - rightHeight() > 1) {
    
    
            // 当前节点 的 左子树 的 右子树的高度 > 左子树的高度
            if (left != null && left.rightHeight() > left.leftHeight()) {
    
    
                // 先对当前节点的左节点进行左旋转
                left.leftRotate();
            }
            rightRotate();
        }
    }

9) Array for testing

        int[] array = {
    
    4, 3, 6, 5, 7, 8};
        int[] array = {
    
    10, 12, 8, 9, 7, 6};
        int[] array = {
    
    10, 11, 7, 6, 8, 9};

10. Multiway search tree

1) The problem of binary tree

  1. The binary tree needs to be loaded into the memory. If there are few nodes in the binary tree, there is no problem, but if there are many nodes in the binary tree (such as 100 million), the following problems exist:

  2. Question 1:

    When building a binary tree, multiple I/O operations are required (massive data exists in databases or files). When building a fork tree with a large number of nodes, the speed will be affected

  3. Question 2:

    A large number of nodes will also cause the height of the binary tree to be very large, which will reduce the operation speed.
    insert image description here

2) Multi-fork tree concept

  1. In a binary tree, each node has data items and at most two child nodes. If each node is allowed to have more data items and more child nodes, it is a multiway tree (multiway tree)
  2. The 2-3 tree we will explain later, the 2-3-4 tree is a multi-fork tree. The multi-fork tree can optimize the binary tree by reorganizing the nodes and reducing the height of the tree.
  3. Give an example (the following 2-3 tree is a multi-fork tree)
    insert image description here

3) B-tree concept

B-tree concept —> link

The B-tree improves efficiency by reorganizing nodes, reducing the height of the tree, and reducing the number of I/O reads and writes.

  1. As shown in Figure B, the tree reduces the height of the tree by reorganizing the nodes
  2. Designers of file systems and database systems use the principle of disk read-ahead to set the size of a node equal to a page (the page size is usually 4k), so that each node can be fully loaded with only one I/O
  3. Set the degree M of the tree (the largest number of child nodes in all nodes) to 1024, and only need 4 I/O operations at most to read the desired elements among the 60 billion elements. B-tree () is widely used in file storage systems and database systems
    insert image description here

4) 2-3 tree concept

  1. All leaf nodes of the 2-3 tree are at the same level. (As long as the B tree meets this condition)
  2. A node with two child nodes is called a second node, and a second node either has no child nodes or has two child nodes.
  3. A node with three child nodes is called a three-node, and a three-node node either has no child nodes or has three child nodes.
  4. A 2-3 tree is a tree consisting of two nodes and three nodes.

5) 2-3 tree application

Build the sequence {16, 24, 12, 32, 14, 26, 34, 10, 8, 28, 38, 20} into a 2-3 tree
insert image description here

insert rules

  1. All leaf nodes of the 2-3 tree are at the same level. (As long as the B tree meets this condition)
  2. A node with two child nodes is called a second node, and a second node either has no child nodes or has two child nodes.
  3. A node with three child nodes is called a three-node, and a three-node either has no child nodes or has three child nodes
  4. When inserting a number into a certain node according to the rules, if the above three requirements cannot be met, it needs to be dismantled, first dismantled upwards, if the upper layer is full, then the current layer is dismantled, and the above 3 conditions still need to be met after dismantling.
  5. The value size of the three-node subtree still obeys the rules of (BST binary sorting tree)

11. B-Tree

1) Concept

The B-tree tree is the B tree, and B means Balanced, which means balance. B-tree is B tree

The 2-3 tree and 2-3-4 tree have been introduced before, they are B-trees (English: B-tree is also written as B-tree), here we will make another explanation, when we are learning Mysql, we often hear that Certain types of indexes are based on B-trees or B+ trees, as shown in the figure:

  1. The order of the B tree: the maximum number of child nodes of the node. For example, the order of the 2-3 tree is 3, and the order of the 2-3-4 tree is 4.

  2. The B-tree search starts from the root node, performs a binary search on the keyword (ordered) sequence in the node, and ends if it hits, otherwise enters the child node of the query keyword's scope;

    Repeat until the corresponding child pointer is empty, or is already a leaf node

  3. Keyword sets are distributed throughout the tree, that is, both leaf nodes and non-leaf nodes store data.

  4. A query may end at a non-leaf node

  5. Its search performance is equivalent to doing a binary search in the complete set of keywords
    insert image description here

12. B+ tree

Detailed concept of B+ tree

1) Concept

The B+ tree is a variant of the B tree and is also a multi-way search tree.
insert image description here

  1. The search of B+ tree is basically the same as that of B tree, the difference is that B+ tree hits only when it reaches the leaf node (B tree can hit at non-leaf nodes), and its performance is equivalent to doing a binary search on the complete set of keywords

  2. All keywords appear in the linked list of leaf nodes (that is, the data can only be in the leaf node [also called dense index]), and the keywords (data) in the linked list happen to be in order.

  3. Impossible to hit on a non-leaf node

  4. Non-leaf nodes are equivalent to the index (sparse index) of leaf nodes. Leaf nodes are equivalent to the data layer for storing (keyword) data

  5. More suitable for file indexing systems

  6. B-tree and B+ tree have their own application scenarios, and it cannot be said that B+ tree is completely better than B-tree, and vice versa.

12. B*Tree

1) Concept

The B* tree is a variant of the B+ tree, and pointers to brothers are added to the non-root and non-leaf nodes of the B+ tree.
insert image description here

  1. The B* tree defines that the number of non-leaf node keywords is at least (2/3)*M, that is, the minimum usage rate of the block is 2/3, and the minimum usage rate of the block of the B+ tree is 1/2 of the B+ tree .
  2. From the first feature, we can see that the probability of allocating new nodes of B* tree is lower than that of B+ tree, and the space utilization rate is higher

Guess you like

Origin blog.csdn.net/m0_56186460/article/details/124545347