Insertion, Searching and Deletion in AVL trees containing a parent node pointer
AVL tree is a self-balancing Binary Search Tree (BST) where the difference between heights of left and right subtrees cannot be more than one for all nodes. The insertion and deletion in AVL trees have been discussed in the previous article. In this article, insert, search, and delete operations are discussed on AVL trees that also have a parent pointer in their structure.
Definition of AVL tree node:
C++
struct AVLwithparent { // Pointer to the left and the // right subtree struct AVLwithparent* left; struct AVLwithparent* right; // Stores the data in the node int key; // Stores the parent pointer struct AVLwithparent* par; // Stores the height of the // current tree int height; } |
Java
class AVLwithParent { // Pointer to the left and the right subtree AVLwithParent left; AVLwithParent right; // Stores the data in the node int key; // Stores the parent pointer AVLwithParent par; // Stores the height of the current tree int height; } |
C#
class AVLwithParent { // Pointer to the left and the right subtree public AVLwithParent left; public AVLwithParent right; // Stores the data in the node public int key; // Stores the parent pointer public AVLwithParent par; // Stores the height of the current tree public int height; } |
Javascript
class AVLwithParent { constructor() { // Pointer to the left and the right subtree this .left = null ; this .right = null ; // Stores the data in the node this .key = null ; // Stores the parent pointer this .par = null ; // Stores the height of the current tree this .height = null ; } } |
Python3
# Python code class AVLwithParent: def __init__( self ): # Pointer to the left and the right subtree self .left = None self .right = None # Stores the data in the node self .key = None # Stores the parent pointer self .par = None # Stores the height of the current tree self .height = None # This code is contributed by princekumaras |
Node: 30, Parent Node: NULL Node: 20, Parent Node: 30 Node: 10, Parent Node: 20 Node: 25, Parent Node: 20 Node: 40, Parent Node: 30 Node: 50, Parent Node: 40
Representation of the Node:
Below is the example of an AVL tree containing a parent pointer:
Insert Operation: The insertion procedure is similar to that of a normal AVL tree without a parent pointer, but in this case, the parent pointers need to be updated with every insertion and rotation accordingly. Follow the steps below to perform insert operation:
- Perform standard BST insert for the node to be placed at its correct position.
- Increase the height of each node encountered by 1 while finding the correct position for the node to be inserted.
- Update the parent and child pointers of the inserted node and its parent respectively.
- Starting from the inserted node till the root node check if the AVL condition is satisfied for each node on this path.
- If w is the node where the AVL condition is not satisfied then we have 4 cases:
- Left Left Case: (If the left subtree of the left child of w has the inserted node)
- Left Right Case: (If the right subtree of the left child of w has the inserted node)
- Right Left Case: (If the left subtree of the right child of w has the inserted node)
- Right Right Case: (If the right subtree of the right child of w has the inserted node)
Below is the implementation of the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // AVL tree node struct AVLwithparent { struct AVLwithparent* left; struct AVLwithparent* right; int key; struct AVLwithparent* par; int height; }; // Function to update the height of // a node according to its children's // node's heights void Updateheight( struct AVLwithparent* root) { if (root != NULL) { // Store the height of the // current node int val = 1; // Store the height of the left // and right subtree if (root->left != NULL) val = root->left->height + 1; if (root->right != NULL) val = max( val, root->right->height + 1); // Update the height of the // current node root->height = val; } } // Function to handle Left Left Case struct AVLwithparent* LLR( struct AVLwithparent* root) { // Create a reference to the // left child struct AVLwithparent* tmpnode = root->left; // Update the left child of the // root to the right child of the // current left child of the root root->left = tmpnode->right; // Update parent pointer of the // left child of the root node if (tmpnode->right != NULL) tmpnode->right->par = root; // Update the right child of // tmpnode to root tmpnode->right = root; // Update parent pointer of // the tmpnode tmpnode->par = root->par; // Update the parent pointer // of the root root->par = tmpnode; // Update tmpnode as the left or the // right child of its parent pointer // according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Right Right Case struct AVLwithparent* RRR( struct AVLwithparent* root) { // Create a reference to the // right child struct AVLwithparent* tmpnode = root->right; // Update the right child of the // root as the left child of the // current right child of the root root->right = tmpnode->left; // Update parent pointer of the // right child of the root node if (tmpnode->left != NULL) tmpnode->left->par = root; // Update the left child of the // tmpnode to root tmpnode->left = root; // Update parent pointer of // the tmpnode tmpnode->par = root->par; // Update the parent pointer // of the root root->par = tmpnode; // Update tmpnode as the left or // the right child of its parent // pointer according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Left Right Case struct AVLwithparent* LRR( struct AVLwithparent* root) { root->left = RRR(root->left); return LLR(root); } // Function to handle right left case struct AVLwithparent* RLR( struct AVLwithparent* root) { root->right = LLR(root->right); return RRR(root); } // Function to insert a node in // the AVL tree struct AVLwithparent* Insert( struct AVLwithparent* root, struct AVLwithparent* parent, int key) { if (root == NULL) { // Create and assign values // to a new node root = new struct AVLwithparent; // If the root is NULL if (root == NULL) { cout << "Error in memory" << endl; } // Otherwise else { root->height = 1; root->left = NULL; root->right = NULL; root->par = parent; root->key = key; } } else if (root->key > key) { // Recur to the left subtree // to insert the node root->left = Insert(root->left, root, key); // Store the heights of the // left and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->left != NULL && key < root->left->key) { // Left Left Case root = LLR(root); } else { // Left Right Case root = LRR(root); } } } else if (root->key < key) { // Recur to the right subtree // to insert the node root->right = Insert(root->right, root, key); // Store the heights of the // left and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->right != NULL && key < root->right->key) { // Right Left Case root = RLR(root); } else { // Right Right Case root = RRR(root); } } } // Case when given key is already // in the tree else { } // Update the height of the // root node Updateheight(root); // Return the root node return root; } // Function to print the preorder // traversal of the AVL tree void printpreorder( struct AVLwithparent* root) { // Print the node's value along // with its parent value cout << "Node: " << root->key << ", Parent Node: " ; if (root->par != NULL) cout << root->par->key << endl; else cout << "NULL" << endl; // Recur to the left subtree if (root->left != NULL) { printpreorder(root->left); } // Recur to the right subtree if (root->right != NULL) { printpreorder(root->right); } } // Driver Code int main() { struct AVLwithparent* root; root = NULL; // Function Call to insert nodes root = Insert(root, NULL, 10); root = Insert(root, NULL, 20); root = Insert(root, NULL, 30); root = Insert(root, NULL, 40); root = Insert(root, NULL, 50); root = Insert(root, NULL, 25); // Function call to print the tree printpreorder(root); } |
Node: 30, Parent Node: NULL Node: 20, Parent Node: 30 Node: 10, Parent Node: 20 Node: 25, Parent Node: 20 Node: 40, Parent Node: 30 Node: 50, Parent Node: 40
Time Complexity: O(log N), where N is the number of nodes of the tree.
Auxiliary Space: O(1)
Search Operation: The search operation in an AVL tree with parent pointers is similar to the search operation in a normal Binary Search Tree. Follow the steps below to perform search operation:
- Start from the root node.
- If the root node is NULL, return false.
- Check if the current node’s value is equal to the value of the node to be searched. If yes, return true.
- If the current node’s value is less than searched key then recur to the right subtree.
- If the current node’s value is greater than searched key then recur to the left subtree.
Below is the implementation of the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // AVL tree node struct AVLwithparent { struct AVLwithparent* left; struct AVLwithparent* right; int key; struct AVLwithparent* par; int height; }; // Function to update the height of // a node according to its children's // node's heights void Updateheight( struct AVLwithparent* root) { if (root != NULL) { // Store the height of the // current node int val = 1; // Store the height of the left // and the right subtree if (root->left != NULL) val = root->left->height + 1; if (root->right != NULL) val = max( val, root->right->height + 1); // Update the height of the // current node root->height = val; } } // Function to handle Left Left Case struct AVLwithparent* LLR( struct AVLwithparent* root) { // Create a reference to the // left child struct AVLwithparent* tmpnode = root->left; // Update the left child of the // root to the right child of the // current left child of the root root->left = tmpnode->right; // Update parent pointer of the left // child of the root node if (tmpnode->right != NULL) tmpnode->right->par = root; // Update the right child of // tmpnode to root tmpnode->right = root; // Update parent pointer of tmpnode tmpnode->par = root->par; // Update the parent pointer of root root->par = tmpnode; // Update tmpnode as the left or // the right child of its parent // pointer according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Right Right Case struct AVLwithparent* RRR( struct AVLwithparent* root) { // Create a reference to the // right child struct AVLwithparent* tmpnode = root->right; // Update the right child of the // root as the left child of the // current right child of the root root->right = tmpnode->left; // Update parent pointer of the right // child of the root node if (tmpnode->left != NULL) tmpnode->left->par = root; // Update the left child of the // tmpnode to root tmpnode->left = root; // Update parent pointer of tmpnode tmpnode->par = root->par; // Update the parent pointer of root root->par = tmpnode; // Update tmpnode as the left or // the right child of its parent // pointer according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Left Right Case struct AVLwithparent* LRR( struct AVLwithparent* root) { root->left = RRR(root->left); return LLR(root); } // Function to handle right left case struct AVLwithparent* RLR( struct AVLwithparent* root) { root->right = LLR(root->right); return RRR(root); } // Function to insert a node in // the AVL tree struct AVLwithparent* Insert( struct AVLwithparent* root, struct AVLwithparent* parent, int key) { if (root == NULL) { // Create and assign values // to a new node root = new struct AVLwithparent; if (root == NULL) { cout << "Error in memory" << endl; } // Otherwise else { root->height = 1; root->left = NULL; root->right = NULL; root->par = parent; root->key = key; } } else if (root->key > key) { // Recur to the left subtree // to insert the node root->left = Insert(root->left, root, key); // Stores the heights of the // left and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->left != NULL && key < root->left->key) { // Left Left Case root = LLR(root); } else { // Left Right Case root = LRR(root); } } } else if (root->key < key) { // Recur to the right subtree // to insert the node root->right = Insert(root->right, root, key); // Store the heights of the left // and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->right != NULL && key < root->right->key) { // Right Left Case root = RLR(root); } else { // Right Right Case root = RRR(root); } } } // Case when given key is // already in tree else { } // Update the height of the // root node Updateheight(root); // Return the root node return root; } // Function to find a key in AVL tree bool AVLsearch( struct AVLwithparent* root, int key) { // If root is NULL if (root == NULL) return false ; // If found, return true else if (root->key == key) return true ; // Recur to the left subtree if // the current node's value is // greater than key else if (root->key > key) { bool val = AVLsearch(root->left, key); return val; } // Otherwise, recur to the // right subtree else { bool val = AVLsearch(root->right, key); return val; } } // Driver Code int main() { struct AVLwithparent* root; root = NULL; // Function call to insert the nodes root = Insert(root, NULL, 10); root = Insert(root, NULL, 20); root = Insert(root, NULL, 30); root = Insert(root, NULL, 40); root = Insert(root, NULL, 50); root = Insert(root, NULL, 25); // Function call to search for a node bool found = AVLsearch(root, 40); if (found) cout << "value found" ; else cout << "value not found" ; return 0; } |
value found
Time Complexity: O(log N), where N is the number of nodes of the tree
Auxiliary Space: O(1)
Delete Operation: The deletion procedure is similar to that of a normal AVL tree without a parent pointer, but in this case, the references to the parent pointers need to be updated with every deletion and rotation accordingly. Follow the steps below to perform the delete operation:
- Perform the delete procedure as in a normal BST.
- From the node that has been deleted, move towards the root.
- At each node on the path, update the height of the node.
- Check for AVL conditions at each node. Let there be 3 nodes: w, x, y where w is the current node, x is the root of the subtree of w which has greater height and y is the root of the subtree of x which has greater height.
- If the node w is unbalanced, there exists one of the following 4 cases:
- Left Left Case (x is left child of w and y is left child of x)
- Left Right Case (x is left child of w and y is right child of x)
- Right Left Case (x is right child of w and y is left child of x)
- Right Right Case (x is right child of w and y is right child of x)
Below is the implementation of the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // AVL tree node struct AVLwithparent { struct AVLwithparent* left; struct AVLwithparent* right; int key; struct AVLwithparent* par; int height; }; // Function to print the preorder // traversal of the AVL tree void printpreorder( struct AVLwithparent* root) { // Print the node's value along // with its parent value cout << "Node: " << root->key << ", Parent Node: " ; if (root->par != NULL) cout << root->par->key << endl; else cout << "NULL" << endl; // Recur to the left subtree if (root->left != NULL) { printpreorder(root->left); } // Recur to the right subtree if (root->right != NULL) { printpreorder(root->right); } } // Function to update the height of // a node according to its children's // node's heights void Updateheight( struct AVLwithparent* root) { if (root != NULL) { // Store the height of the // current node int val = 1; // Store the height of the left // and right subtree if (root->left != NULL) val = root->left->height + 1; if (root->right != NULL) val = max( val, root->right->height + 1); // Update the height of the // current node root->height = val; } } // Function to handle Left Left Case struct AVLwithparent* LLR( struct AVLwithparent* root) { // Create a reference to the // left child struct AVLwithparent* tmpnode = root->left; // Update the left child of the // root to the right child of the // current left child of the root root->left = tmpnode->right; // Update parent pointer of left // child of the root node if (tmpnode->right != NULL) tmpnode->right->par = root; // Update the right child of // tmpnode to root tmpnode->right = root; // Update parent pointer of tmpnode tmpnode->par = root->par; // Update the parent pointer of root root->par = tmpnode; // Update tmpnode as the left or // the right child of its parent // pointer according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Right Right Case struct AVLwithparent* RRR( struct AVLwithparent* root) { // Create a reference to the // right child struct AVLwithparent* tmpnode = root->right; // Update the right child of the // root as the left child of the // current right child of the root root->right = tmpnode->left; // Update parent pointer of the // right child of the root node if (tmpnode->left != NULL) tmpnode->left->par = root; // Update the left child of the // tmpnode to root tmpnode->left = root; // Update parent pointer of tmpnode tmpnode->par = root->par; // Update the parent pointer of root root->par = tmpnode; // Update tmpnode as the left or // the right child of its parent // pointer according to its key value if (tmpnode->par != NULL && root->key < tmpnode->par->key) { tmpnode->par->left = tmpnode; } else { if (tmpnode->par != NULL) tmpnode->par->right = tmpnode; } // Make tmpnode as the new root root = tmpnode; // Update the heights Updateheight(root->left); Updateheight(root->right); Updateheight(root); Updateheight(root->par); // Return the root node return root; } // Function to handle Left Right Case struct AVLwithparent* LRR( struct AVLwithparent* root) { root->left = RRR(root->left); return LLR(root); } // Function to handle right left case struct AVLwithparent* RLR( struct AVLwithparent* root) { root->right = LLR(root->right); return RRR(root); } // Function to balance the tree after // deletion of a node struct AVLwithparent* Balance( struct AVLwithparent* root) { // Store the current height of // the left and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // If current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (firstheight < secondheight) { // Store the height of the // left and right subtree // of the current node's // right subtree int rightheight1 = 0; int rightheight2 = 0; if (root->right->right != NULL) rightheight2 = root->right->right->height; if (root->right->left != NULL) rightheight1 = root->right->left->height; if (rightheight1 > rightheight2) { // Right Left Case root = RLR(root); } else { // Right Right Case root = RRR(root); } } else { // Store the height of the // left and right subtree // of the current node's // left subtree int leftheight1 = 0; int leftheight2 = 0; if (root->left->right != NULL) leftheight2 = root->left->right->height; if (root->left->left != NULL) leftheight1 = root->left->left->height; if (leftheight1 > leftheight2) { // Left Left Case root = LLR(root); } else { // Left Right Case root = LRR(root); } } } // Return the root node return root; } // Function to insert a node in // the AVL tree struct AVLwithparent* Insert( struct AVLwithparent* root, struct AVLwithparent* parent, int key) { if (root == NULL) { // Create and assign values // to a new node root = new struct AVLwithparent; if (root == NULL) cout << "Error in memory" << endl; else { root->height = 1; root->left = NULL; root->right = NULL; root->par = parent; root->key = key; } } else if (root->key > key) { // Recur to the left subtree // to insert the node root->left = Insert(root->left, root, key); // Store the heights of the // left and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->left != NULL && key < root->left->key) { // Left Left Case root = LLR(root); } else { // Left Right Case root = LRR(root); } } } else if (root->key < key) { // Recur to the right subtree // to insert the node root->right = Insert(root->right, root, key); // Store the heights of the left // and right subtree int firstheight = 0; int secondheight = 0; if (root->left != NULL) firstheight = root->left->height; if (root->right != NULL) secondheight = root->right->height; // Balance the tree if the // current node is not balanced if ( abs (firstheight - secondheight) == 2) { if (root->right != NULL && key < root->right->key) { // Right Left Case root = RLR(root); } else { // Right Right Case root = RRR(root); } } } // Case when given key is // already in tree else { } // Update the height of the // root node Updateheight(root); // Return the root node return root; } // Function to delete a node from // the AVL tree struct AVLwithparent* Delete( struct AVLwithparent* root, int key) { if (root != NULL) { // If the node is found if (root->key == key) { // Replace root with its // left child if (root->right == NULL && root->left != NULL) { if (root->par != NULL) { if (root->par->key < root->key) root->par->right = root->left; else root->par->left = root->left; // Update the height // of root's parent Updateheight(root->par); } root->left->par = root->par; // Balance the node // after deletion root->left = Balance( root->left); return root->left; } // Replace root with its // right child else if (root->left == NULL && root->right != NULL) { if (root->par != NULL) { if (root->par->key < root->key) root->par->right = root->right; else root->par->left = root->right; // Update the height // of the root's parent Updateheight(root->par); } root->right->par = root->par; // Balance the node after // deletion root->right = Balance(root->right); return root->right; } // Remove the references of // the current node else if (root->left == NULL && root->right == NULL) { if (root->par->key < root->key) { root->par->right = NULL; } else { root->par->left = NULL; } if (root->par != NULL) Updateheight(root->par); root = NULL; return NULL; } // Otherwise, replace the // current node with its // successor and then // recursively call Delete() else { struct AVLwithparent* tmpnode = root; tmpnode = tmpnode->right; while (tmpnode->left != NULL) { tmpnode = tmpnode->left; } int val = tmpnode->key; root->right = Delete(root->right, tmpnode->key); root->key = val; // Balance the node // after deletion root = Balance(root); } } // Recur to the right subtree to // delete the current node else if (root->key < key) { root->right = Delete(root->right, key); root = Balance(root); } // Recur into the right subtree // to delete the current node else if (root->key > key) { root->left = Delete(root->left, key); root = Balance(root); } // Update height of the root if (root != NULL) { Updateheight(root); } } // Handle the case when the key to be // deleted could not be found else { cout << "Key to be deleted " << "could not be found\n" ; } // Return the root node return root; } // Driver Code int main() { struct AVLwithparent* root; root = NULL; // Function call to insert the nodes root = Insert(root, NULL, 9); root = Insert(root, NULL, 5); root = Insert(root, NULL, 10); root = Insert(root, NULL, 0); root = Insert(root, NULL, 6); // Print the tree before deleting node cout << "Before deletion:\n" ; printpreorder(root); // Function Call to delete node 10 root = Delete(root, 10); // Print the tree after deleting node cout << "After deletion:\n" ; printpreorder(root); } |
Before deletion: Node: 9, Parent Node: NULL Node: 5, Parent Node: 9 Node: 0, Parent Node: 5 Node: 6, Parent Node: 5 Node: 10, Parent Node: 9 After deletion: Node: 6, Parent Node: NULL Node: 5, Parent Node: 6 Node: 0, Parent Node: 5 Node: 9, Parent Node: 6
Time Complexity: O(log N), where N is the number of nodes of the tree
Auxiliary Space: O(1)
Please Login to comment...