Open In App

POTD Solutions | 23 Nov’ 23 | AVL Tree Insertion

Last Updated : 24 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Welcome to the daily solutions of our PROBLEM OF THE DAY (POTD). We will discuss the entire problem step-by-step and work towards developing an optimized solution. This will not only help you brush up on your concepts of AVL Trees but will also help you build up problem-solving skills.

23-nov

POTD Solution 23 November 2023

We recommend you to try this problem on our GeeksforGeeks Practice portal first, and maintain your streak to earn Geeksbits and other exciting prizes, before moving towards the solution.

POTD 23 November: AVL Tree Insertion:

Given an AVL tree and N values to be inserted in the tree. Write a function to insert elements into the given AVL tree.

Note:

  • The tree will be checked after each insertion. 
  • If it violates the properties of balanced BST, an error message will be printed followed by the inorder traversal of the tree at that moment.
  • If instead all insertions are successful, inorder traversal of the tree will be printed.

Example:

Input:
N = 3
Values to be inserted = {5,1,4}
Output:
1 4 5
Explanation:
Value to be inserted = 5
5
Value to be inserted = 1
5
/
1
Value to be inserted = 4
5 4
/ LR rotation / \
1 ———–> 1 5
\
4
Therefore the inorder of the final tree will be 1, 4, 5.

Input:
N = 7
Values to be inserted = {21,26,30,9,4,14,28}
Output:
4 9 14 21 26 28 30
Explanation:
Value to be inserted = 21
21
Value to be inserted = 26
21
\
26
Value to be inserted = 30
21 26
\ LL rotation / \
26 ———–> 21 30
\
30
Value to be inserted = 9
26
/ \
21 30
/
9
Value to be inserted = 4
26 26
/ \ / \
21 30 9 30
/ RR rotation / \
9 ———–> 4 21
/
4
Value to be inserted = 14
26 21
/ \ / \
9 30 9 26
/ \ LR rotation / \ \
4 21 ———–> 4 14 30
/
14
Value to be inserted = 28
21 21
/ \ / \
9 26 9 28
/ \ \ RL rotation / \ / \
4 14 30 ———–> 4 14 26 30
/
28
Therefore the inorder of the final tree will be 4, 9, 14, 21, 26, 28, 30.

Insertion in an AVL Tree:

Steps to follow for insertion:

Let the newly inserted node be w 

  • Perform standard BST insert for w
  • Starting from w, travel up and find the first unbalanced node. Let z be the first unbalanced node, be the child of z that comes on the path from w to z and x be the grandchild of z that comes on the path from to z
  • Re-balance the tree by performing appropriate rotations on the subtree rooted with z. There can be 4 possible cases that need to be handled as x, y and z can be arranged in 4 ways.
  • Following are the possible 4 arrangements: 
    • y is the left child of z and x is the left child of y (Left Left Case) 
    • y is the left child of z and x is the right child of y (Left Right Case) 
    • y is the right child of z and x is the right child of y (Right Right Case) 
    • y is the right child of z and x is the left child of y (Right Left Case)

Following are the operations to be performed in above mentioned 4 cases. In all of the cases, we only need to re-balance the subtree rooted with z and the complete tree becomes balanced as the height of the subtree (After appropriate rotations) rooted with z becomes the same as it was before insertion.

1. Left Left Case 

T1, T2, T3 and T4 are subtrees.
z y
/ \ / \
y T4 Right Rotate (z) x z
/ \ – – – – – – – – -> / \ / \
x T3 T1 T2 T3 T4
/ \
T1 T2

2. Left Right Case 

z z x
/ \ / \ / \
y T4 Left Rotate (y) x T4 Right Rotate(z) y z
/ \ – – – – – – – – -> / \ – – – – – – – -> / \ / \
T1 x y T3 T1 T2 T3 T4
/ \ / \
T2 T3 T1 T2

3. Right Right Case 

z y
/ \ / \
T1 y Left Rotate(z) z x
/ \ – – – – – – – -> / \ / \
T2 x T1 T2 T3 T4
/ \
T3 T4

4. Right Left Case 

z z x
/ \ / \ / \
T1 y Right Rotate (y) T1 x Left Rotate(z) z y
/ \ – – – – – – – – -> / \ – – – – – – – -> / \ / \
x T4 T2 y T1 T2 T3 T4
/ \ / \
T2 T3 T3 T4

Illustration of Insertion at AVL Tree:

Approach:

The idea is to use recursive BST insert, after insertion, we get pointers to all ancestors one by one in a bottom-up manner. So we don’t need a parent pointer to travel up. The recursive code itself travels up and visits all the ancestors of the newly inserted node. 

Follow the steps mentioned below to implement the idea:

  • Perform the normal BST insertion. 
  • The current node must be one of the ancestors of the newly inserted node. Update the height of the current node. 
  • Get the balance factor (left subtree height – right subtree height) of the current node. 
  • If the balance factor is greater than 1, then the current node is unbalanced and we are either in the Left Left case or left Right case. To check whether it is left left case or not, compare the newly inserted key with the key in the left subtree root
  • If the balance factor is less than -1, then the current node is unbalanced and we are either in the Right Right case or Right-Left case. To check whether it is the Right Right case or not, compare the newly inserted key with the key in the right subtree root.

Below is the implementation of the above approach:

C++




class Solution{
  public:
    // Function to get the height of a node
    int height(struct Node *N)
    {
        if (N == NULL)
            return 0;
        return N->height;
    }
      
    // Function to perform right rotation
    Node *rightRotate(Node *y)
    {
        Node *x = y->left;
        Node *T2 = x->right;
      
        // Perform rotation
        x->right = y;
        y->left = T2;
      
        // Update heights
        y->height = max(height(y->left), height(y->right))+1;
        x->height = max(height(x->left), height(x->right))+1;
      
        return x;
    }
      
    // Function to perform left rotation
    Node *leftRotate(struct Node *x)
    {
        struct Node *y = x->right;
        struct Node *T2 = y->left;
      
        // Perform rotation
        y->left = x;
        x->right = T2;
      
        // Update heights
        x->height = max(height(x->left), height(x->right))+1;
        y->height = max(height(y->left), height(y->right))+1;
      
        return y;
    }
      
    // Function to get the balance factor of a node
    int getBalance(struct Node *N)
    {
        if (N == NULL)
            return 0;
        return height(N->left) - height(N->right);
    }
      
    // Function to check if a tree is height-balanced
    bool __getBalance__(struct Node *root)
    {
        int lh; // for height of left subtree
        int rh; // for height of right subtree 
          
        // If tree is empty, return true
        if(root == NULL)
            return 1; 
      
        // Get the height of left and right subtrees
        lh = height(root->left);
        rh = height(root->right);
      
        // Check if the difference in heights is <= 1
        // and recursively check for all nodes in the tree
        if( abs(lh-rh) <= 1 &&
            __getBalance__(root->left) &&
            __getBalance__(root->right))
         return 1;
      
         // If we reach here, the tree is not height-balanced
        return 0;
    }
      
    // Function to insert a new node into an AVL tree
    Node* insertToAVL(Node* node, int data)
    {   
        // If the node is null, create a new node with the data
        if (node == NULL)
            return new Node(data);
      
        // If the data is less than the current node's data,
        // insert it into the left subtree
        if (data < node->data)
            node->left = insertToAVL(node->left, data);
        // If the data is greater than the current node's data,
        // insert it into the right subtree
        else if(data > node->data)
            node->right = insertToAVL(node->right, data);
       // If the data is equal to the current node's data,
       // return the node (no insertion needed)
       else
           return node;
      
        // Update the height of the current node
        node->height = max(height(node->left), height(node->right)) + 1;
      
        // Check the balance factor of the current node
        int balance = getBalance(node);
      
        // Perform rotations based on the balance factor
          
        // Left Left Case
        if (balance > 1 && data < node->left->data)
            return rightRotate(node);
      
       // Right Right Case
        if (balance < -1 && data > node->right->data)
            return leftRotate(node);
      
        // Left Right Case
        if (balance > 1 && data > node->left->data)
        {
            node->left = leftRotate(node->left);
            return rightRotate(node);
        }
      
        // Right Left Case
        if (balance < -1 && data < node->right->data)
        {
            node->right = rightRotate(node->right);
            return leftRotate(node);
        }
      
        return node;
    }
};


Java




class Solution {
    // Function to get the height of a node
    int height(Node N)
    {
        if (N == null)
            return 0;
        return N.height;
    }
  
    // Function to perform right rotation
    Node rightRotate(Node y)
    {
        Node x = y.left;
        Node T2 = x.right;
  
        // Perform rotation
        x.right = y;
        y.left = T2;
  
        // Update heights
        y.height
            = Math.max(height(y.left), height(y.right)) + 1;
        x.height
            = Math.max(height(x.left), height(x.right)) + 1;
  
        return x;
    }
  
    // Function to perform left rotation
    Node leftRotate(Node x)
    {
        Node y = x.right;
        Node T2 = y.left;
  
        // Perform rotation
        y.left = x;
        x.right = T2;
  
        // Update heights
        x.height
            = Math.max(height(x.left), height(x.right)) + 1;
        y.height
            = Math.max(height(y.left), height(y.right)) + 1;
  
        return y;
    }
  
    // Function to get the balance factor of a node
    int getBalance(Node N)
    {
        if (N == null)
            return 0;
        return height(N.left) - height(N.right);
    }
  
    // Function to check if a tree is height-balanced
    boolean isBalanced(Node root)
    {
        int lh, rh;
  
        // If tree is empty, return true
        if (root == null)
            return true;
  
        // Get the height of left and right subtrees
        lh = height(root.left);
        rh = height(root.right);
  
        // Check if the difference in heights is <= 1
        // and recursively check for all nodes in the tree
        if (Math.abs(lh - rh) <= 1 && isBalanced(root.left)
            && isBalanced(root.right))
            return true;
  
        // If we reach here, the tree is not height-balanced
        return false;
    }
  
    // Function to insert a new node into an AVL tree
    Node insertToAVL(Node node, int data)
    {
        // If the node is null, create a new node with the
        // data
        if (node == null)
            return new Node(data);
  
        // If the data is less than the current node's data,
        // insert it into the left subtree
        if (data < node.data)
            node.left = insertToAVL(node.left, data);
        // If the data is greater than the current node's
        // data, insert it into the right subtree
        else if (data > node.data)
            node.right = insertToAVL(node.right, data);
        // If the data is equal to the current node's data,
        // return the node (no insertion needed)
        else
            return node;
  
        // Update the height of the current node
        node.height = Math.max(height(node.left),
                               height(node.right))
                      + 1;
  
        // Check the balance factor of the current node
        int balance = getBalance(node);
  
        // Perform rotations based on the balance factor
  
        // Left Left Case
        if (balance > 1 && data < node.left.data)
            return rightRotate(node);
  
        // Right Right Case
        if (balance < -1 && data > node.right.data)
            return leftRotate(node);
  
        // Left Right Case
        if (balance > 1 && data > node.left.data) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }
  
        // Right Left Case
        if (balance < -1 && data < node.right.data) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }
  
        return node;
    }
}


Python3




class Solution:
    def height(self, N):
        if N is None:
            return 0
        return N.height
  
    # Function to perform right rotation
    def rightRotate(self, y):
        x = y.left
        T2 = x.right
  
        # Perform rotation
        x.right = y
        y.left = T2
  
        # Update heights
        y.height = max(self.height(y.left), self.height(y.right)) + 1
        x.height = max(self.height(x.left), self.height(x.right)) + 1
  
        return x
  
    # Function to perform left rotation
    def leftRotate(self, x):
        y = x.right
        T2 = y.left
  
        # Perform rotation
        y.left = x
        x.right = T2
  
        # Update heights
        x.height = max(self.height(x.left), self.height(x.right)) + 1
        y.height = max(self.height(y.left), self.height(y.right)) + 1
  
        return y
  
    # Function to get the balance factor of a node
    def getBalance(self, N):
        if N is None:
            return 0
        return self.height(N.left) - self.height(N.right)
  
    # Function to check if a tree is height-balanced
    def isBalanced(self, root):
        if root is None:
            return True
  
        # Get the height of left and right subtrees
        lh = self.height(root.left)
        rh = self.height(root.right)
  
        # Check if the difference in heights is <= 1
        # and recursively check for all nodes in the tree
        if abs(lh - rh) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right):
            return True
  
        # If we reach here, the tree is not height-balanced
        return False
  
    # Function to insert a new node into an AVL tree
    def insertToAVL(self, node, data):
        # If the node is None, create a new node with the data
        if node is None:
            return Node(data)
  
        # If the data is less than the current node's data,
        # insert it into the left subtree
        if data < node.data:
            node.left = self.insertToAVL(node.left, data)
        # If the data is greater than the current node's data,
        # insert it into the right subtree
        elif data > node.data:
            node.right = self.insertToAVL(node.right, data)
        # If the data is equal to the current node's data,
        # return the node (no insertion needed)
        else:
            return node
  
        # Update the height of the current node
        node.height = max(self.height(node.left), self.height(node.right)) + 1
  
        # Check the balance factor of the current node
        balance = self.getBalance(node)
  
        # Perform rotations based on the balance factor
  
        # Left Left Case
        if balance > 1 and data < node.left.data:
            return self.rightRotate(node)
  
        # Right Right Case
        if balance < -1 and data > node.right.data:
            return self.leftRotate(node)
  
        # Left Right Case
        if balance > 1 and data > node.left.data:
            node.left = self.leftRotate(node.left)
            return self.rightRotate(node)
  
        # Right Left Case
        if balance < -1 and data < node.right.data:
            node.right = self.rightRotate(node.right)
            return self.leftRotate(node)
  
        return node


Time Complexity: O(log(n)), For Insertion
Auxiliary Space: O(1)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads