In extreme cases, a binary search tree evolves into a tree with only one child on one side, such as a left or right child for each non-leaf. When searching, you need to traverse the tree to find the target value. Its fast search value is not reflected. If the search tree balances the height difference between left and right subtrees when it is built, If the height difference between left and right subtrees is less than 1, then its search efficiency is O(lgn). Balanced binary tree is such a tree. It has the following characteristics:
1. The height difference between left and right subtrees should not exceed 1
2. Left and right subtrees are balanced binary trees, and empty nodes consider them a balanced binary tree.
The key feature of balanced binary trees is that the height difference between left and right subtrees whose roots are any one of the nodes cannot exceed 1. Because of this feature limitation, when creating a balanced binary tree, it is necessary to rotate the tree to restore its balance. There are several specific cases of rotation:
1. LL type rotation (one-way right): In this case, the left subtree of the left child of a node causes the node to be unbalanced, and the node needs to be rotated to the right, as shown in the following figure:
As shown in the figure above, a node with a value of 3, when it has only one left child, the height difference between the left and right children is 1. When 1 is inserted again, the height difference between the left and right children becomes 2 and needs to be rotated to the right:
LL_Rotate(node):
leftNode=node.left;// Left child of node to rotate
leftRightNode=leftNode;// Right child of left child to rotate node
leftNode.right=node;// Update the left child node and make yourself the right node of the left child, that is, the right child of 2 in Figure 3 above
node.left=leftRightNode;// Update your left child,
node.height=max(node.left.height,node.right.height)+1;// Update Height
leftNode.height=max(leftNode.left.height,leftNode.right.height)+1;// With the new root node height, the node node needs to be updated first...
return leftNode; // Return to the left child's place to replace yourself, e.g. in the picture above, 2 replaces 3
2. RR type rotation (one-way left): In this case, the right subtree of a node's right child causes the node to become unbalanced and needs to be rotated to the left, as shown in the following figure:
As shown above, because 3 inserts, the balance factor of root node 1 becomes 2, requiring 1 to rotate left in the opposite direction to LL:
RR_Rotate(node):
rightNode=node.right;// Get Right Child
rightLeftNode=rightNode.left;// Get the left child of the right child
rightNode.left=node;// Update Right Child
node.right=rightLeftNode;
node.height=max(node.left.height,node.right.height)+1;
rightNode.height=max(rightNode.left.height,right.right.height)+1;
; return rightNode; // Return
3. LR type rotation (first left, then right): In this case, the right subtree of a left child of a node causes the node to be unbalanced, so the left child of the node needs to be rotated left, then rotated right, as shown in the following figure:
As shown in the figure above, the left child of a node with a node value of 8, because the insertion of 6 causes an imbalance of 8 nodes, needs to rotate the left child 4 first, replace the position of 4 with 6, then rotate 8 with right, and replace 8 with 6:
Left_Right_Rotate(node):
node.left=LL_Rotate(node.left); // Turn left for left child
return RR_Rotate(node);// Turn yourself right
4. RL type rotation (first right, then left): In this case, the left subtree of the right child of a node causes the node to be unbalanced, so the right child of the node needs to be rotated right, and then left, as shown in the following figure:
As shown in the figure above, the balance factor of 4 is broken by adding a left child node 6 to its right child 7, which requires that node 7 be rotated right first and then left-rotated to 4, as opposed to LR, with the pseudocode as follows:
Right_Left_Rotate(node):
node.right=RR_Rotate(node.right);// Turn right for right child
return LL_Rotate(node);// Turn left on yourself
Getting the height of a node:
Get_Height(node):
if node==null:
return 0;
leftHeight=Get_Height(node.left);
rightHeight=Get_Height(node.right);
return max(leftHeight,rightHeight)+1;
Obtaining the balance factor, the balance factor of a node is determined by the height difference between left and right subtrees:
Get_Factor(node):
return Get_Height(node.left)-Get_Height(node.right);
The balance adjustment of nodes involves left, right, left and right rotation, right and left rotation:
balance(node):
if node==null
return null;
//Left subtree causes imbalance
if getFactor(node)==2 :
//Left child's left subtree causes imbalance
if getFactor(node.left)==1:
node=LL_Rotate(node)
else:
//Left child's right subtree causes imbalance
node=Left_Right_Rotate(node);
//Right subtree causes imbalance
else if getFactor(node)==-2:
The right child of the right subtree causes imbalance
if getFactor(node.right)==-1:
node=RR_Rotate(node);
else
//Right child's left subtree causes imbalance
node=Right_Left_Rotate(node);
//Return rotated nodes
return node;
Insertion of tree:
insert_val(node,val):
if node==null:
return new Node(val);
//Here is a recursive creation process
if val<node.val:
node.left=insert_val(node.left,val);
node.left.parent=node;
else
node.right=insert_val(node.right,val);
node.right.parent=node;
//Return adjusted node
return balance(node);
Tree deletion operation, deletion operation is a bit complex, you need to find the node to replace your own, its method is to find the maximum value of the left subtree of the deleted node; If the node to be deleted does not have a left subtree, it needs to be found
The minimum value of the right subtree, if the node to be deleted is a leaf node, it can be deleted directly:
delete_node(node,val):
if node==null:
return;// Not found, go back directly
if val < node.val:
delete_node(node.left,val);
else if val > node.val:
delete_node(node.right,val);
else: //Target node found
//The target node is a leaf node
if node.left==null && node.right==null:
parent=node.parent;
//The node to be deleted is the root node
if parent==null:
root=null;
else if parent.left==node:
parent.left=null;
Other: //Delete Nodes
parent.right=null;
else if node.left!=null:
left=node.left
maxNode=left
while left!=null:
maxNode=right;
left=left.right;
node.val=maxNode.val
delete_node(node.left,node.val);
Other: //with node.left above!= Null, instead, changes the left to right
balance(node);// Adjust the balance of trees
The above AVL tree is built and analyzed. The key point is the balanced operation of nodes. Recursive operation is used when creating and deleting nodes. It is a design technique, as follows: Complete sample code:
package avl; import java.util.ArrayList; import java.util.List; /** * AVL Definition of tree */ public class AVLTree { //root node Node root=null; /** * Insert a new value * @param val * @return */ public AVLTree insertVal(int val){ if(root==null){ root=new Node(val); }else{ insertVal(root,val); } return this; } /** * Insertion of Nodes * @param node * @param val * @return */ private Node insertVal(Node node,int val){ if(node==null){ node=new Node(val); return node; } if(val<node.val){ Node left=insertVal(node.left,val); node.left=left; left.parent=node; }else{ Node right=insertVal(node.right,val); node.right=right; right.parent=node; } //Adjust Nodes node=balance(node); return node; } /** * Delete Node * @param val */ public void deleteVal(int val){ deleteVal(root,val); } private void deleteVal(Node node,int val){ //Not found, go back directly if(node==null){ return; }else if(val<node.val){ deleteVal(node.left,val); balance(node); }else if(val>node.val){ deleteVal(node.right,val); balance(node); }else{ //Leaf node, delete directly if(node.left==null && node.right==null){ Node parent=node.parent; if(parent==null){ root=null; } if(parent.left==node){ parent.left=null; }else{ parent.right=null; } }else{ //If the left subtree is not empty, find its largest successor node if(node.left!=null){ Node left=node.left; Node maxNode=left; //Notice how this finds the largest successor node while(left!=null){ maxNode=left; left=left.right; } node.val=maxNode.val; deleteVal(node.left,maxNode.val); balance(node); }else{ Node right=node.right; Node maxNode=right; while(right!=null){ maxNode=right; right=right.left; } node.val=maxNode.val; deleteVal(node.right,maxNode.val); balance(node); } } } } /** * Action of balancing nodes * @param node * @return */ private Node balance(Node node){ if(node==null){ return null; } if(getFactor(node)==2){ if(getFactor(node.left)==1){ node= LL_Rotate(node); }else{ node= LR_Rotate(node); } }else if(getFactor(node)==-2){ if(getFactor(node.right)==-1){ node= RR_Rotate(node); }else{ node= RL_Rotate(node); } } return node; } /** * Get the height of the node * @param node * @return */ private int getHeight(Node node){ if(node==null){ return 0; } int left=getHeight(node.left); int right=getHeight(node.right); int max=Math.max(left,right); return max+1; } /** * Get the Balance Factor of Nodes * @param node * @return */ private int getFactor(Node node){ if(node==null){ return 0; } return getHeight(node.left)-getHeight(node.right); } /** * First right, then left * @param node * @return */ private Node RL_Rotate(Node node){ Node right=LL_Rotate(node.right); node.right=right; right.parent=node; return RR_Rotate(node); } /** * Left then Right * @param node * @return */ private Node LR_Rotate(Node node){ Node left=RR_Rotate(node.left); node.left=left; left.parent=node; return LL_Rotate(node); } /** * One-way Left Rotation * @param node * @return */ private Node RR_Rotate(Node node){ Node right=node.right,parent=node.parent; Node rightLeft=right.left; right.left=node; node.parent=right; node.right=rightLeft; if(rightLeft!=null){ rightLeft.parent=node; } right.parent=parent; if(parent!=null){ if(parent.left==node){ parent.left=right; }else{ parent.right=right; } }else{ root=right; } return right; } /** * One-way Right Rotation * @param node * @return */ private Node LL_Rotate(Node node){ Node left=node.left,parent=node.parent; Node leftRight=left.right; left.right=node; node.parent=left; node.left=leftRight; if(leftRight!=null){ leftRight.parent=node; } left.parent=parent; if(parent!=null){ if(parent.left==node){ parent.left=left; }else{ parent.right=left; } }else{ root=left; } return left; } /** * Preorder traversal * @param node */ public void preOrder(Node node){ if(node!=null){ System.out.print(node); preOrder(node.left); preOrder(node.right); } } /** * Intermediate traversal * @param node */ public void inOrder(Node node){ if(node!=null){ inOrder(node.left); System.out.print(node); inOrder(node.right); } } /** * Post-order traversal * @param node */ public void postOrder(Node node){ if(node!=null){ postOrder(node.left); postOrder(node.right); System.out.print(node); } } /** * Traversing trees layer by layer */ public void printByLevel(){ System.out.println("========================="); List<Node> temp=new ArrayList<>(); if(root!=null){ temp.add(root); } while(temp.size()>0){ List<Node> nodes=new ArrayList<>(); temp.stream().forEach(obj-> { System.out.print(obj); if(obj.left!=null){ nodes.add(obj.left); } if(obj.right!=null){ nodes.add(obj.right); } }); System.out.println(); temp.clear(); temp.addAll(nodes); } } public static void main(String[] args) { AVLTree tree=new AVLTree(); tree.insertVal(1).insertVal(2).insertVal(3).insertVal(4).insertVal(5).insertVal(7).insertVal(6); tree.printByLevel(); tree.deleteVal(6); tree.printByLevel(); tree.deleteVal(4); tree.printByLevel(); } } class Node{ public int val; public Node left,right,parent; public Node(int val){ this.val=val; } @Override public String toString() { return val+" "; } }