[key and difficult points] [data structure 01] binary sort tree and balanced binary tree
1, Binary sort tree
1. Introduction
Binary Sort Tree (BST), for any non leaf node of the Binary Sort Tree, the value of the left child node is required to be smaller than that of the current node, and the value of the right child node is required to be larger than that of the current node. If you have the same value, you can put the node on the left child node or the right child node
2. Create
- Take the first value of the array as the root node
- If the next value is smaller than the root node, it recurses to the left subtree until the current node has no left child node, so it is the left child node of the current node
- If the next value is larger than the root node, it recurses to the right subtree until the current node has no right child node, so it is the right child node of the current node
- Repeat the above two steps until the array traversal is completed
3. Delete node
Delete leaf node
- Find the targetNode to delete
- Find the parent node of targetNode
- Determines whether the targetNode is the left child node or the right child node of the parent
- Delete according to conditions
Left child node: parent.left = null
Right child node: parent.right = null
Delete a node with only one child node
- Find the targetNode to delete
- Find the parent node of targetNode
- Determine whether the child node of the targetNode is A left child node or A right child node (the left is marked as A and the right is marked as B)
- Determine whether the target is the left child node or the right child node of the parent (the left is marked as 1 and the right is marked as 2)
- Delete according to conditions
A1: parent.left = targetNode.left
A2: parent.right = targetNode.left
B1: parent.left = targetNode.right
B2: parent.right = targetNode.right
Delete a node with two child nodes
- Find the targetNode to delete
- Find the parent node of targetNode
- Recursively find the smallest nearest node minVal in the leaf node in the right subtree of the targetNode
- Create a temporary variable temp, temp = minVal
- Delete minVal
- targetNode.value =temp
4. Code implementation
package com.sisyphus.BinaryTree; public class BinarySortTreeDemo { public static void main(String[] args) { int[] arr = {7,3,10,12,5,1,9,0}; BinarySortTree binarySortTree = new BinarySortTree(); for(int i : arr){ binarySortTree.add(new Node(i)); } System.out.println("Middle order traversal:"); binarySortTree.infixOrder(); binarySortTree.delNode(10); System.out.println("After deleting a node"); binarySortTree.infixOrder(); } } class Node{ int value; Node left; Node right; public Node(int value){ this.value = value; } @Override public String toString() { return "Node{" + "value=" + value + '}'; } public Node search(int value){ if(value ==this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } } public Node 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; } } } public void add(Node node){ if(node == null){ return; } if(node.value < this.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); } } } //Medium order traversal public void infixOrder(){ if(this.left != null){ this.left.infixOrder(); } System.out.println(this); if (this.right != null){ this.right.infixOrder(); } } } class BinarySortTree{ private Node root; public Node search(int value){ if(root == null){ return null; }else{ return root.search(value); } } public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } } public int delReturnMin(Node node){ while(node.left != null){ node = node.left; } delNode(node.value); return node.value; } public void add(Node node){ if(root == null){ root = node; }else{ root.add(node); } } //Medium order traversal public void infixOrder(){ if(root == null){ System.out.println("The tree is empty and cannot be traversed"); }else{ root.infixOrder(); } } //Delete Vertex public void delNode(int value){ if(root == null){ return; }else{ Node targetNode = root.search(value); if(targetNode == null){ return; } Node parent = searchParent(value); //Delete leaf node if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value == value){ parent.left = null; }else{ parent.right = null; } } //Delete a node with two child nodes else if(targetNode.left != null && targetNode.right != null){ int minVal = delReturnMin(targetNode.right); targetNode.value = minVal; } //Delete a node with only one child node else{ if(targetNode.left != null){ if(parent != null){ if(parent.left.value == value){ parent.left = targetNode.left; }else{ parent.right = targetNode.left; } }else{ root = targetNode.left; } }else{ if(parent != null){ if(parent.left.value == value){ parent.left = targetNode.right; }else{ parent.right = targetNode.right; } }else{ root = targetNode.right; } } } } } }
2, Balanced binary tree
1. Introduction
The binary sort tree can improve the search efficiency to some extent, but when the original sequence is ordered, such as sequence A = {1, 3, 5, 7, 9}. The binary sort tree constructed according to this sequence is a right oblique tree. At the same time, the binary tree degenerates into a single linked list, and the search efficiency is reduced to O(n)
The search efficiency of binary sort tree depends on the height of the tree, so keeping the height of the tree to the minimum can ensure the search efficiency of the tree. When the number of nodes is fixed, the balance between the left and right ends of the tree is maintained, and the search efficiency of the tree is the highest, so there is a balanced binary tree
2. Introduction
Balanced Binary Tree, also known as AVL tree, was proposed by the mathematicians adelse velskil and Landis of the former Soviet Union in 1962. It is an empty tree or the absolute value of the height difference between its left and right subtrees is no more than 1, and both the left and right subtrees are a Balanced Binary Tree
Equilibrium factor
The height (depth) difference between the left subtree and the right subtree of a node is the Balance Factor of the node. There is no node with a Balance Factor greater than 1 in the balanced binary tree. In a balanced binary tree, the balance factors of nodes can only take 0, 1 and - 1, respectively corresponding to the equal height of left and right subtrees, the higher the left subtree and the higher the right subtree
Minimum unbalanced subtree
The subtree with the root of the node whose absolute value of the first balance factor exceeds 1 is called the minimum unbalanced subtree. In other words, an unbalanced tree may have multiple subtrees unbalanced at the same time. At this time, as long as we adjust the smallest unbalanced subtree, we can adjust the unbalanced tree to a balanced tree
The imbalance adjustment of balanced binary tree is mainly realized by rotating the minimum imbalance subtree. There are two processing methods according to the direction of rotation: left rotation and right rotation
Sinistral
Idea:
- Replace the current node with the right child node of the current node
- The left subtree of the right child node becomes the right subtree of the current node
- The current node becomes the left subtree of the right child node
private void leftRotate(){ Node newNode = new Node(value); newNode.left = left; newNode.right = rightn.left; value = right.value; right = right.right; left = newNode; }
The following three figures are the actual left-handed process, which is helpful to understand the code
Dextral
Idea:
- Replace the current node with the left child node of the current node
- The right subtree of the left child node becomes the left subtree of the current node
- The current node becomes the right subtree of the left sub node
private void rightRotate(){ Node newNode = new Node(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; }
The following three figures are the actual right-handed process, which is helpful to understand the code
3. Insert node
There are four situations in which a balanced binary tree inserts nodes
Insertion mode | describe | Rotation mode |
---|---|---|
LL | Inserting nodes into the left subtree of the root node of the left subtree of A destroys the balance | Right rotation |
RR | Inserting A node into the right subtree of the root node of the right subtree of A destroys the balance | Left rotation |
LR | Inserting A node into the right subtree of the root node of the left subtree of A destroys the balance | First left and then right |
RL | Inserting A node into the left subtree of the root node of the right subtree of A destroys the balance | First right and then left |
LL and RR have been mentioned in the above introduction. We focus on LR and RL
Both LR and RL cannot be rebalanced through one selection, as shown in the figure below
The left subtree of A is higher than the right subtree. If only one right rotation is performed, the change is:
We find that the right subtree of B is higher than the left subtree again
In order to rebalance the balanced binary tree whose insertion mode is LR or RL, we need to take the following two methods
For LR, turn left and then right
- Rotate the left child node of the root node
- Rotate the root node right
For RL. First right and then left
- Rotate the right child node of the root node
- Rotate the root node to the left
4. Delete node
The deletion operations of balanced binary tree and binary sort tree are the same, which are divided into four cases. However, after deleting nodes, the balanced binary tree needs to check the balance again and correct it
The difference between the delete operation and the balance correction after the insert operation is that after the insert operation, only the first non-equilibrium point popped up in the insert stack needs to be corrected, while the delete operation needs to correct all non-equilibrium points in the stack
For the correction of unbalanced state caused by deletion, it can be understood as follows: the deletion of left subtree or right subtree is equivalent to the insertion of right subtree or left subtree, and then select the corresponding rotation according to the four cases of upper insertion
5. Code implementation
package com.sisyphus.BinaryTree.AVLTree; public class AVLTreeDemo { public static void main(String[] args){ int[] arr = {10,11,7,6,8,9}; AVLTree avlTree = new AVLTree(); //Add node for(int i = 0; i < arr.length; i++){ avlTree.add(new Node(arr[i])); } System.out.println("Medium order traversal"); avlTree.infixOrder(); System.out.println("Before deleting a node:"); System.out.println("Tree height:"+ avlTree.getRoot().height()); System.out.println("Left subtree height of tree:" + avlTree.getRoot().leftHeight()); System.out.println("Height of right subtree of tree:" + avlTree.getRoot().rightHeight()); System.out.println("The current root node is:" + avlTree.getRoot()); // //Test delete a node // avlTree.delNode(7); // System.out.println("middle order traversal after deleting nodes"); // avlTree.infixOrder(); // // //Test delete the second node // avlTree.delNode(10); // System.out.println("middle order traversal after deleting nodes:"); // avlTree.infixOrder(); //After deleting two nodes, the test will show an imbalance, and then the balance tree will be adjusted automatically avlTree.delNode(6); avlTree.delNode(7); System.out.println("Middle order traversal after deleting nodes"); avlTree.infixOrder(); System.out.println("Tree height:" + avlTree.getRoot().height()); System.out.println("Left subtree height of tree:" + avlTree.getRoot().leftHeight()); System.out.println("Height of right subtree of tree:" + avlTree.getRoot().rightHeight()); System.out.println("The current root node is:" + avlTree.getRoot()); System.out.println("The left child node of the current root node is:" + avlTree.getRoot().left); System.out.println("The right child node of the current root node is:" + avlTree.getRoot().right); System.out.println("The left child node and the right child node of the current root node are:" + avlTree.getRoot().left.right); } } class Node{ int value; Node left; Node right; public Node(int value){ this.value = value; } @Override public String toString() { return "Node{" + "value=" + value + '}'; } public Node search(int value){ if(value ==this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } } public Node 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; } } } //Medium order traversal public void infixOrder(){ if(this.left != null){ this.left.infixOrder(); } System.out.println(this); if (this.right != null){ this.right.infixOrder(); } } //Returns the height of the tree with the current node as the root node public int height(){ return Math.max(left == null ? 0: left.height(), right == null ? 0 : right.height()) + 1; } //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(); } //Left rotation public void leftRotate(){ //Create a new node with the value of the root node of the current subtree Node newNode = new Node(value); //Set the left subtree of the new node as the left subtree of the current node newNode.left = left; //Set the right sub node of the new node as the left sub tree of the right sub node of the current node newNode.right = right.left; //Replace the value of the root node of the current subtree with the value of the right child node value = right.value; //Set the right subtree of the current node as the right subtree of the right child node right = right.right; //Replace the left child node of the current node with a new node left = newNode; } //Right rotation public void rightRotate(){ Node newNode = new Node(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; } public void add(Node node){ if(node == null){ return; } if(node.value < this.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); } } //Different from the binary sort tree, the height difference between the left and right subtrees needs to be judged after adding nodes, and adjusted according to the situation //If (right subtree height - left subtree height) > 1, rotate left if(rightHeight() - leftHeight() > 1){ //If the left subtree height of the right subtree of the current node is greater than its right subtree height, adjust its right subtree first, that is, rotate it right if(right != null && right.leftHeight() > right.rightHeight()){ //First, rotate the right child node of the current node right.rightRotate(); //Then rotate the current node to the left leftRotate(); }else{ leftRotate(); return; } } //If (left subtree height - right subtree height) > 1, rotate right if(leftHeight() - rightHeight() > 1){ //If the right subtree height of the left subtree of the current node is greater than its left subtree height, first adjust its left subtree, that is, rotate left if(left != null && left.rightHeight() > left.leftHeight()){ //First, rotate the left child node of the current node left.leftRotate(); //Then right rotate the current node rightRotate(); }else{ rightRotate(); } } } } class AVLTree{ private Node root; public Node getRoot(){ return root; } public void setRoot(Node root){ this.root = root; } public Node search(int value){ if(root == null){ return null; }else{ return root.search(value); } } public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } } public int delReturnMin(Node node){ while(node.left != null){ node = node.left; } delNode(node.value); return node.value; } public void add(Node node){ if(root == null){ root = node; }else{ root.add(node); } } //Medium order traversal public void infixOrder(){ if(root == null){ System.out.println("The tree is empty and cannot be traversed"); }else{ root.infixOrder(); } } //Delete Vertex public void delNode(int value){ if(root == null){ return; }else{ Node targetNode = root.search(value); if(targetNode == null){ return; } Node parent = searchParent(value); //Delete leaf node if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value == value){ parent.left = null; }else{ parent.right = null; } } //Delete a node with two child nodes else if(targetNode.left != null && targetNode.right != null){ int minVal = delReturnMin(targetNode.right); targetNode.value = minVal; } //Delete a node with only one child node else{ if(targetNode.left != null){ if(parent != null){ if(parent.left.value == value){ parent.left = targetNode.left; }else{ parent.right = targetNode.left; } }else{ root = targetNode.left; } }else{ if(parent != null){ if(parent.left.value == value){ parent.left = targetNode.right; }else{ parent.right = targetNode.right; } }else{ root = targetNode.right; } } } } //Different from the binary sort tree, the height difference between the left and right subtrees needs to be judged and adjusted according to the situation after deleting the node //If (right subtree height - left subtree height) > 1, rotate left if(root.rightHeight() - root.leftHeight() > 1){ //If the height of the left subtree of the right subtree of the current node is greater than its right subtree, first adjust its right subtree, that is, rotate it right if(root.right != null && root.right.leftHeight() > root.right.rightHeight()){ //First, rotate the right child node of the current node root.right.rightRotate(); //Then rotate the current node to the left root.leftRotate(); }else{ root.leftRotate(); return; } } //If (left subtree height - right subtree height) > 1, rotate right if(root.leftHeight() - root.rightHeight() > 1){ //If the right subtree height of the left subtree of the current node is greater than its left subtree height, first adjust its left subtree, that is, rotate left if(root.left != null && root.left.rightHeight() > root.left.leftHeight()){ //First, rotate the left child node of the current node root.left.leftRotate(); //Then right rotate the current node root.rightRotate(); }else{ root.rightRotate(); } } } }