AVL trees are self-balancing binary search trees. This means that whenever an
Note: To learn more about AVL trees 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
Efficiently inserting a new node in an AVL tree requires a recursive approach that involves 2 main steps:
Inserting the node itself: We insert a node into the tree the same way it would be inserted into a binary search tree—recursively. This new node will become a leaf node.
Rebalancing the tree once the node is inserted: This happens once the backtracking starts. Starting from the newly inserted node (currently a leaf node), we travel up the tree until the first unbalanced node (with a
To insert a node in a BST, we first compare the value of
The example below demonstrates this (the current root node during the recursive calls is colored orange):
Once the node is inserted into the tree, the backtracking can begin since the base case is reached for the first time.
During backtracking, we check the balance factor of each node. If the balance factor is in the range [-1, 1], we do nothing and allow backtracking to proceed as it is. However, if the node is unbalanced, we need to perform rotation(s) on it to rebalance it.
Note: To learn more about how the rebalancing of an AVL tree is done using rotations, refer to this link.
If the balance factor is 2, then this means that the unbalanced node's left subtree is heavier than the right one. Thus, there is either a left-left imbalance or a left-right imbalance. Now, if the balance factor of the unbalanced node's left child is greater than or equal to 1 (or, alternatively, if the value of
Similarly, if the balance factor is -2, then this means that the unbalanced node's right subtree is heavier than the left one. Thus, there is either a right-right imbalance or a right-left imbalance. Now, if the balance factor of the unbalanced node's right child is less than or equal to 0 (or, alternatively, if the value of
This backtracking process is depicted below, continuing the same example from above (the current root node during each step of the backtracking is colored red):
Once the backtracking ends, we have successfully inserted new_node
. Another example is demonstrated below:
The insertNode()
function, highlighted in the lines below, implements the steps discussed above for inserting a node in an AVL tree:
#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){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;}}}Node * insertNode(Node * root, Node * new_node){// inserting the nodeif (root == NULL){root = new_node;return root;}if (new_node->value < root->value){root->left_child = insertNode(root->left_child, new_node);}else if (new_node->value > root->value){root->right_child = insertNode(root->right_child, new_node);}// node has been inserted// checking if node needs to be rebalancedint bf = getBalanceFactor(root);if(bf!=-1 && bf!=0 && bf!=1) // if true, then node needs to be rebalanced{root = balanceTree(bf, root);}return root; // backtrack}};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 = new Node(30);Node * n2 = new Node(10);Node * n3 = new Node(50);Node * n4 = new Node(8);Node * n5 = new Node(18);Node * n6 = new Node(58);Node * n7 = new Node(5);Node * n8 = new Node(6);obj.root = obj.insertNode(obj.root, n1);obj.root = obj.insertNode(obj.root, n2);obj.root = obj.insertNode(obj.root, n3);obj.root = obj.insertNode(obj.root, n4);obj.root = obj.insertNode(obj.root, n5);obj.root = obj.insertNode(obj.root, n6);obj.root = obj.insertNode(obj.root, n7);obj.root = obj.insertNode(obj.root, n8);prettyPrintTree(obj.root, 1);return 0 ;}
In the output of the code above, 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