AVL trees are self-balancing binary search trees. This means that whenever an
NOTE: To learn more about what an AVL tree is, and the different types of imbalances that can arise in them due to the insertion/deletion of a node(s), refer to this link.
This maintenance of balance in an AVL tree allows operations such as insertion, deletion, and searching to run in
AVL trees can maintain this balance by keeping track of the
There are two categories of rotations, which each contain 2 types of rotations. Each rotation deals with a particular type of imbalance that may arise.
There are 2 rotations in this category:
Right rotation—to deal with the left-left imbalance.
Left rotation—to deal with the right-right imbalance.
Given an unbalanced node
There are 2 rotations in this category:
Right-left rotation—to deal with the right-left imbalance.
Left-right rotation—to deal with the left-right imbalance.
As the name suggests, these rotations are a combination of two single rotations applied after each other
Given an unbalanced node
Then, the second rotation involves making
The diagrams below illustrate how the left-right rotation and the right-left rotation work:
We will use a hard-coded AVL tree to demonstrate how these rotations are used for rebalancing. This AVL tree will have a left-left imbalance and is depicted below:
Note: Although the rebalancing of an AVL tree can happen after every insertion/deletion, here, we will build our hard-coded tree such that it only needs to happen once. The tree we will build will always be balanced until the 6th node (with
) is inserted. The tree is constructed this way to demonstrate rebalancing at work, in isolation from a proper AVL tree insertion implementation.
Once the tree is built, we'll rebalance it via the balanceTree()
function. The image below demonstrates the effect of this rebalancing:
The C++ code below implements the 4 rotations discussed above:
#include <iostream>using namespace std;class Node{public:int value;Node * left_child;Node * right_child;Node(){value = 0;left_child = NULL;right_child = NULL;}Node(int v){value = v;left_child = NULL;right_child = NULL;}};class AVLtree{public:Node * root;AVLtree(){root = NULL;}int height(Node * r){if (r == NULL){return -1;}else{int left_subtree_height = height(r->left_child);int right_subtree_height = height(r->right_child);if (left_subtree_height > right_subtree_height){return (left_subtree_height + 1);}else{return (right_subtree_height + 1);}}}int getBalanceFactor(Node * n){if (n == NULL){return -1;}return height(n->left_child) - height(n->right_child);}Node * rightRotate(Node * x){Node * y = x->left_child;Node * T2 = y->right_child;// Perform rotationy->right_child = x;x->left_child = T2;return y;}Node * leftRotate(Node * x){Node * y = x->right_child;Node * T2 = y->left_child;// Perform rotationy->left_child = x;x->right_child = T2;return y;}Node * balanceTree(int balance_factor, Node * unbalanced_node , Node * latest_inserted_node){if (balance_factor > 1){// left-left imbalanceif(getBalanceFactor(unbalanced_node->left_child) >= 0){unbalanced_node = rightRotate(unbalanced_node);return unbalanced_node;}// left-right imbalanceelse if(getBalanceFactor(unbalanced_node->left_child) == -1){unbalanced_node->left_child = leftRotate(unbalanced_node->left_child);unbalanced_node = rightRotate(unbalanced_node);return unbalanced_node;}}if (balance_factor < -1){// right-right imbalanceif(getBalanceFactor(unbalanced_node->right_child) <= -0){unbalanced_node = leftRotate(unbalanced_node);return unbalanced_node;}// right-left imbalanceelse if(getBalanceFactor(unbalanced_node->right_child) == 1){unbalanced_node->right_child = rightRotate(unbalanced_node->right_child);unbalanced_node = leftRotate(unbalanced_node);return unbalanced_node;}}}};void prettyPrintTree(Node * r, int space){if (r == NULL){return;}space += 10;prettyPrintTree(r->right_child, space);cout << endl;for (int i = 10; i < space; i++){cout << " ";}cout << r->value << "\n";prettyPrintTree(r->left_child, space);}int main(){AVLtree obj;//Node n1;Node * n1 = new Node();//Node n2;Node * n2 = new Node();//Node n3;Node * n3 = new Node();//Node n4;Node * n4 = new Node();//Node n5;Node * n5 = new Node();//Node n6;Node * n6 = new Node();n1->value = 20;n2->value = 15;n3->value = 30;n4->value = 10;n5->value = 17;n6->value = 7;// First level (root 20):obj.root = n1 ;// Second level (root 20, left_child 15, right_child 30)obj.root->left_child = n2 ;obj.root->right_child = n3;// Third level (root 15, left_child 10, right_child 17):obj.root->left_child->left_child = n4;obj.root->left_child->right_child = n5 ;// Fourth level (root 10, left_child 7, right_child NULL):obj.root->left_child->left_child->left_child = n6 ;// This tree is unbalanced (at the root node 20, which// has a balance factor of +2).cout << "Tree before balancing:" << endl ;prettyPrintTree(obj.root , 1);int bf = obj.getBalanceFactor(obj.root); // bf=2obj.root = obj.balanceTree(bf, obj.root , n6);cout << "\n==================================================================" << endl ;cout << "Tree after balancing:" << endl ;prettyPrintTree(obj.root , 1);return 0 ;}
Note: In the output of the above code, the nodes of the tree have been printed in a 2-D manner to help with visualization. To keep the implementation of the
prettyPrintTree()
function straightforward, the tree is rotated 90o to the left in the output.
Free Resources