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 tr...
Query and deletion of binary tree
Sequential storage binary tree
Cued binary tree
huffman tree
Binary sort tree (BST)
Balanced binary tree (AVL)
summary

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[]; 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 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[]; 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.

20 September 2021, 21:38 | Views: 2314

Add new comment

For adding a comment, please log in
or create account

0 comments