Category: Tree Data Structure

  • B Trees

    B trees are extended binary search trees that are specialized in m-way searching, since the order of B trees is ‘m’. Order of a tree is defined as the maximum number of children a node can accommodate. Therefore, the height of a b tree is relatively smaller than the height of AVL tree and RB tree.

    They are general form of a Binary Search Tree as it holds more than one key and two children.

    The various properties of B trees include −

    • Every node in a B Tree will hold a maximum of m children and (m-1) keys, since the order of the tree is m.
    • Every node in a B tree, except root and leaf, can hold at least m/2 children
    • The root node must have no less than two children.
    • All the paths in a B tree must end at the same level, i.e. the leaf nodes must be at the same level.
    • A B tree always maintains sorted data.
    b trees

    B trees are also widely used in disk access, minimizing the disk access time since the height of a b tree is low.

    Note − A disk access is the memory access to the computer disk where the information is stored and disk access time is the time taken by the system to access the disk memory.

    Basic Operations of B Trees

    The operations supported in B trees are Insertion, deletion and searching with the time complexity of O(log n) for every operation.

    Insertion operation

    The insertion operation for a B Tree is done similar to the Binary Search Tree but the elements are inserted into the same node until the maximum keys are reached. The insertion is done using the following procedure −

    Step 1 − Calculate the maximum (m−1) and, minimum (⌈m2⌉−1) number of keys a node can hold, where m is denoted by the order of the B Tree.

    Calculate min max

    Step 2 − The data is inserted into the tree using the binary search insertion and once the keys reach the maximum number, the node is split into half and the median key becomes the internal node while the left and right keys become its children.

    data_inserted

    Step 3 − All the leaf nodes must be on the same level.

    leaf nodes same level

    The keys, 5, 3, 21, 9, 13 are all added into the node according to the binary search property but if we add the key 22, it will violate the maximum key property. Hence, the node is split in half, the median key is shifted to the parent node and the insertion is then continued.

    adding 11

    Another hiccup occurs during the insertion of 11, so the node is split and median is shifted to the parent.

    adding 16

    While inserting 16, even if the node is split in two parts, the parent node also overflows as it reached the maximum keys. Hence, the parent node is split first and the median key becomes the root. Then, the leaf node is split in half the median of leaf node is shifted to its parent.

    final B tree

    The final B tree after inserting all the elements is achieved.

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    // C Program for B trees #include <stdio.h>#include <stdlib.h>structBTree{//node declarationint*d;structBTree**child_ptr;int l;int n;};structBTree*r =NULL;structBTree*np =NULL;structBTree*x =NULL;//creation of nodestructBTree*init(){int i;
       np =(structBTree*)malloc(sizeof(structBTree));//order 6
       np->d =(int*)malloc(6*sizeof(int));
       np->child_ptr =(structBTree**)malloc(7*sizeof(structBTree*));
       np->l =1;
       np->n =0;for(i =0; i <7; i++){
          np->child_ptr[i]=NULL;}return np;}//traverse the treevoidtraverse(structBTree*p){printf("\n");int i;for(i =0; i < p->n; i++){if(p->l ==0){traverse(p->child_ptr[i]);}printf(" %d", p->d[i]);}if(p->l ==0){traverse(p->child_ptr[i]);}printf("\n");}//sort the treevoidsort(int*p,int n){int i, j, t;for(i =0; i < n; i++){for(j = i; j <= n; j++){if(p[i]> p[j]){
                t = p[i];
                p[i]= p[j];
                p[j]= t;}}}}intsplit_child(structBTree*x,int i){int j, mid;structBTree*np1,*np3,*y;
       np3 =init();//create new node
       np3->l =1;if(i ==-1){
          mid = x->d[2];//find mid
          x->d[2]=0;
          x->n--;
          np1 =init();
          np1->l =0;
          x->l =1;for(j =3; j <6; j++){
             np3->d[j -3]= x->d[j];
             np3->child_ptr[j -3]= x->child_ptr[j];
             np3->n++;
             x->d[j]=0;
             x->n--;}for(j =0; j <6; j++){
             x->child_ptr[j]=NULL;}
          np1->d[0]= mid;
          np1->child_ptr[np1->n]= x;
          np1->child_ptr[np1->n +1]= np3;
          np1->n++;
          r = np1;}else{
          y = x->child_ptr[i];
          mid = y->d[2];
          y->d[2]=0;
          y->n--;for(j =3; j <6; j++){
             np3->d[j -3]= y->d[j];
             np3->n++;
             y->d[j]=0;
             y->n--;}
          x->child_ptr[i +1]= y;
          x->child_ptr[i +1]= np3;}return mid;}voidinsert(int a){int i, t;
       x = r;if(x ==NULL){
          r =init();
          x = r;}else{if(x->l ==1&& x->n ==6){
             t =split_child(x,-1);
             x = r;for(i =0; i < x->n; i++){if(a > x->d[i]&& a < x->d[i +1]){
                   i++;break;}elseif(a < x->d[0]){break;}else{continue;}}
             x = x->child_ptr[i];}else{while(x->l ==0){for(i =0; i < x->n; i++){if(a > x->d[i]&& a < x->d[i +1]){
                      i++;break;}elseif(a < x->d[0]){break;}else{continue;}}if(x->child_ptr[i]->n ==6){
                   t =split_child(x, i);
                   x->d[x->n]= t;
                   x->n++;continue;}else{
                   x = x->child_ptr[i];}}}}
       x->d[x->n]= a;sort(x->d, x->n);
       x->n++;}intmain(){int i, n, t;insert(10);insert(20);insert(30);insert(40);insert(50);printf("Insertion Done");printf("\nB tree:");traverse(r);return0;}

    Output

    Insertion Done
    B tree:
     10 20 30 40 50
    

    Deletion operation

    The deletion operation in a B tree is slightly different from the deletion operation of a Binary Search Tree. The procedure to delete a node from a B tree is as follows −

    Case 1 − If the key to be deleted is in a leaf node and the deletion does not violate the minimum key property, just delete the node.

    delete key 14
    deleted key 14

    Case 2 − If the key to be deleted is in a leaf node but the deletion violates the minimum key property, borrow a key from either its left sibling or right sibling. In case if both siblings have exact minimum number of keys, merge the node in either of them.

    delete key 13
    deleted key 3

    Case 3 − If the key to be deleted is in an internal node, it is replaced by a key in either left child or right child based on which child has more keys. But if both child nodes have minimum number of keys, theyre merged together.

    delete key 13
    deleted key 13

    Case 4 − If the key to be deleted is in an internal node violating the minimum keys property, and both its children and sibling have minimum number of keys, merge the children. Then merge its sibling with its parent.

    delete key 5
    deleted key 5

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    //deletion operation in BTree#include <stdio.h>#include <stdlib.h>#define MAX 3#define MIN 2structBTreeNode{int item[MAX +1], count;structBTreeNode*linker[MAX +1];};structBTreeNode*root;// creating nodestructBTreeNode*createNode(int item,structBTreeNode*child){structBTreeNode*newNode;
       newNode =(structBTreeNode*)malloc(sizeof(structBTreeNode));
       newNode->item[1]= item;
       newNode->count =1;
       newNode->linker[0]= root;
       newNode->linker[1]= child;return newNode;}// adding value to the nodevoidaddValToNode(int item,int pos,structBTreeNode*node,structBTreeNode*child){int j = node->count;while(j > pos){
       node->item[j +1]= node->item[j];
       node->linker[j +1]= node->linker[j];
       j--;}
       node->item[j +1]= item;
       node->linker[j +1]= child;
       node->count++;}// Spliting the nodevoidsplitNode(int item,int*pval,int pos,structBTreeNode*node,structBTreeNode*child,structBTreeNode**newNode){int median, j;if(pos > MIN)
        median = MIN +1;else
        median = MIN;*newNode =(structBTreeNode*)malloc(sizeof(structBTreeNode));
      j = median +1;while(j <= MAX){(*newNode)->item[j - median]= node->item[j];(*newNode)->linker[j - median]= node->linker[j];
        j++;}
      node->count = median;(*newNode)->count = MAX - median;if(pos <= MIN){addValToNode(item, pos, node, child);}else{addValToNode(item, pos - median,*newNode, child);}*pval = node->item[node->count];(*newNode)->linker[0]= node->linker[node->count];
      node->count--;}// Set the value in the nodeintsetValueInNode(int item,int*pval,structBTreeNode*node,structBTreeNode**child){int pos;if(!node){*pval = item;*child =NULL;return1;}if(item < node->item[1]){
       pos =0;}else{for(pos = node->count;(item < node->item[pos]&& pos >1); pos--);if(item == node->item[pos]){printf("Duplicates not allowed\n");return0;}}if(setValueInNode(item, pval, node->linker[pos], child)){if(node->count < MAX){addValToNode(*pval, pos, node,*child);}else{splitNode(*pval, pval, pos, node,*child, child);return1;}}return0;}// inserting elements in BTreevoidinsert(int item){int flag, i;structBTreeNode*child;   
       flag =setValueInNode(item,&i, root,&child);if(flag)
          root =createNode(i, child);}// Copy the successorvoidcopySuccessor(structBTreeNode*myNode,int pos){structBTreeNode*dummy;
       dummy = myNode->linker[pos];for(; dummy->linker[0]!=NULL;)
          dummy = dummy->linker[0];
       myNode->item[pos]= dummy->item[1];}// Remove the value in BTreevoidremoveVal(structBTreeNode*myNode,int pos){int i = pos +1;while(i <= myNode->count){
          myNode->item[i -1]= myNode->item[i];
          myNode->linker[i -1]= myNode->linker[i];
          i++;}
       myNode->count--;}// right shiftvoidrightShift(structBTreeNode*myNode,int pos){structBTreeNode*x = myNode->linker[pos];int j = x->count;while(j >0){
          x->item[j +1]= x->item[j];
          x->linker[j +1]= x->linker[j];}
       x->item[1]= myNode->item[pos];
       x->linker[1]= x->linker[0];
       x->count++;   
       x = myNode->linker[pos -1];
       myNode->item[pos]= x->item[x->count];
       myNode->linker[pos]= x->linker[x->count];
       x->count--;return;}// left shiftvoidleftShift(structBTreeNode*myNode,int pos){int j =1;structBTreeNode*x = myNode->linker[pos -1];  
       x->count++;
       x->item[x->count]= myNode->item[pos];
       x->linker[x->count]= myNode->linker[pos]->linker[0];   
       x = myNode->linker[pos];
       myNode->item[pos]= x->item[1];
       x->linker[0]= x->linker[1];
       x->count--;while(j <= x->count){
          x->item[j]= x->item[j +1];
          x->linker[j]= x->linker[j +1];
          j++;}return;}// Merge the nodesvoidmergeNodes(structBTreeNode*myNode,int pos){int j =1;structBTreeNode*x1 = myNode->linker[pos],*x2 = myNode->linker[pos   -1];   
       x2->count++;
       x2->item[x2->count]= myNode->item[pos];
       x2->linker[x2->count]= myNode->linker[0];while(j <= x1->count){
          x2->count++;
          x2->item[x2->count]= x1->item[j];
          x2->linker[x2->count]= x1->linker[j];
          j++;
       j = pos;while(j < myNode->count){
           myNode->item[j]= myNode->item[j +1];
           myNode->linker[j]= myNode->linker[j +1];
           j++;}
       myNode->count--;free(x1);}}// Adjust the node in BTreevoidadjustNode(structBTreeNode*myNode,int pos){if(!pos){if(myNode->linker[1]->count > MIN){leftShift(myNode,1);}else{mergeNodes(myNode,1);}}else{if(myNode->count != pos){if(myNode->linker[pos -1]->count > MIN){rightShift(myNode, pos);}else{if(myNode->linker[pos +1]->count > MIN){leftShift(myNode, pos +1);}else{mergeNodes(myNode, pos);}}}else{if(myNode->linker[pos -1]->count > MIN)rightShift(myNode, pos);elsemergeNodes(myNode, pos);}}}// Delete a value from the nodeintdelValFromNode(int item,structBTreeNode*myNode){int pos, flag =0;if(myNode){if(item < myNode->item[1]){
          pos =0;
          flag =0;}else{for(pos = myNode->count;(item < myNode->item[pos]&& pos >1); pos--);if(item == myNode->item[pos]){
                flag =1;}else{
                flag =0;}}if(flag){if(myNode->linker[pos -1]){copySuccessor(myNode, pos);
              flag =delValFromNode(myNode->item[pos], myNode->linker[pos]);if(flag ==0){printf("Given data is not present in B-Tree\n");}}else{removeVal(myNode, pos);}}else{
          flag =delValFromNode(item, myNode->linker[pos]);}if(myNode->linker[pos]){if(myNode->linker[pos]->count < MIN)adjustNode(myNode, pos);}}return flag;}// Delete operaiton in BTreevoiddelete(int item,structBTreeNode*myNode){structBTreeNode*tmp;if(!delValFromNode(item, myNode)){printf("Not present\n");return;}else{if(myNode->count ==0){
             tmp = myNode;
             myNode = myNode->linker[0];free(tmp);}}
       root = myNode;return;}voiddisplay(structBTreeNode*myNode){int i;if(myNode){for(i =0; i < myNode->count; i++){display(myNode->linker[i]);printf("%d ", myNode->item[i +1]);}display(myNode->linker[i]);}}intmain(){int item, ch;insert(8);insert(9);insert(10);insert(11);insert(15);insert(16);insert(17);insert(18);insert(20);insert(23);printf("Insertion Done");printf("\nBTree elements before deletion: \n");display(root);int ele =20;printf("\nThe element to be deleted: %d", ele);delete(ele, root);printf("\nBTree elements before deletion: \n");display(root);}

    Output

    Insertion Done
    BTree elements before deletion: 
    8 9 10 11 15 16 17 18 20 23 
    The element to be deleted: 20
    BTree elements before deletion: 
    8 9 10 11 15 16 17 18 23 8 9 23 
  • Red Black Trees

    Red-Black Trees are another type of the Balanced Binary Search Trees with two coloured nodes: Red and Black. It is a self-balancing binary search tree that makes use of these colours to maintain the balance factor during the insertion and deletion operations. Hence, during the Red-Black Tree operations, the memory uses 1 bit of storage to accommodate the colour information of each node

    In Red-Black trees, also known as RB trees, there are different conditions to follow while assigning the colours to the nodes.

    • The root node is always black in colour.
    • No two adjacent nodes must be red in colour.
    • Every path in the tree (from the root node to the leaf node) must have the same amount of black coloured nodes.

    Even though AVL trees are more balanced than RB trees, with the balancing algorithm in AVL trees being stricter than that of RB trees, multiple and faster insertion and deletion operations are made more efficient through RB trees.

    RB trees

    Fig: RB trees

    Basic Operations of Red-Black Trees

    The operations on Red-Black Trees include all the basic operations usually performed on a Binary Search Tree. Some of the basic operations of an RB Tree include −

    • Insertion
    • Deletion
    • Search

    Insertion operation

    Insertion operation of a Red-Black tree follows the same insertion algorithm of a binary search tree. The elements are inserted following the binary search property and as an addition, the nodes are color coded as red and black to balance the tree according to the red-black tree properties.

    Follow the procedure given below to insert an element into a red-black tree by maintaining both binary search tree and red black tree properties.

    Case 1 − Check whether the tree is empty; make the current node as the root and color the node black if it is empty.

    Case 2 − But if the tree is not empty, we create a new node and color it red. Here we face two different cases −

    • If the parent of the new node is a black colored node, we exit the operation and tree is left as it is.
    • If the parent of this new node is red and the color of the parent’s sibling is either black or if it does not exist, we apply a suitable rotation and recolor accordingly.
    • If the parent of this new node is red and color of the parent’s sibling is red, recolor the parent, the sibling and grandparent nodes to black. The grandparent is recolored only if it is not the root node; if it is the root node recolor only the parent and the sibling.

    Example

    Let us construct an RB Tree for the first 7 integer numbers to understand the insertion operation in detail −

    The tree is checked to be empty so the first node added is a root and is colored black.

    first node

    Now, the tree is not empty so we create a new node and add the next integer with color red,

    new node

    The nodes do not violate the binary search tree and RB tree properties, hence we move ahead to add another node.

    The tree is not empty; we create a new red node with the next integer to it. But the parent of the new node is not a black colored node,

    third node

    The tree right now violates both the binary search tree and RB tree properties; since parent’s sibling is NULL, we apply a suitable rotation and recolor the nodes.

    suitable rotation

    Now that the RB Tree property is restored, we add another node to the tree −

    RB Tree property

    The tree once again violates the RB Tree balance property, so we check for the parent’s sibling node color, red in this case, so we just recolor the parent and the sibling.

    RB Tree balance property

    We next insert the element 5, which makes the tree violate the RB Tree balance property once again.

    insert element 5

    And since the sibling is NULL, we apply suitable rotation and recolor.

    sibling is NULL

    Now, we insert element 6, but the RB Tree property is violated and one of the insertion cases need to be applied −

    insert element 6

    The parent’s sibling is red, so we recolor the parent, parent’s sibling and the grandparent nodes since the grandparent is not the root node.

    recolor parent

    Now, we add the last element, 7, but the parent node of this new node is red.

    add last element

    Since the parent’s sibling is NULL, we apply suitable rotations (RR rotation)

    RB Tree achieved

    The final RB Tree is achieved.

    Deletion operation

    The deletion operation on red black tree must be performed in such a way that it must restore all the properties of a binary search tree and a red black tree. Follow the steps below to perform the deletion operation on the red black tree −

    Firstly, we perform deletion based on the binary search tree properties.

    Case 1 − If either the node to be deleted or the node’s parent is red, just delete it.

    Case 2 − If the node is a double black, just remove the double black (double black occurs when the node to be deleted is a black colored leaf node, as it adds up the NULL nodes which are considered black colored nodes too)

    Case 3 − If the double black’s sibling node is also a black node and its child nodes are also black in color, follow the steps below −

    • Remove double black
    • Recolor its parent to black (if the parent is a red node, it becomes black; if the parent is already a black node, it becomes double black)
    • Recolor the parent’s sibling with red
    • If double black node still exists, we apply other cases.

    Case 4 − If the double black node’s sibling is red, we perform the following steps −

    • Swap the colors of the parent node and the parent’s sibling node.
    • Rotate parent node in the double black’s direction
    • Reapply other cases that are suitable.

    Case 5 − If the double black’s sibling is a black node but the sibling’s child node that is closest to the double black is red, follows the steps below −

    • Swap the colors of double black’s sibling and the sibling’s child in question
    • Rotate the sibling node is the opposite direction of double black (i.e. if the double black is a right child apply left rotations and vice versa)
    • Apply case 6.

    Case 6 − If the double black’s sibling is a black node but the sibling’s child node that is farther to the double black is red, follows the steps below −

    • Swap the colors of double black’s parent and sibling nodes
    • Rotate the parent in double black’s direction (i.e. if the double black is a right child apply right rotations and vice versa)
    • Remove double black
    • Change the color of red child node to black.

    Example

    Considering the same constructed Red-Black Tree above, let us delete few elements from the tree.

    RB Tree achieved

    Delete elements 4, 5, 3 from the tree.

    To delete the element 4, let us perform the binary search deletion first.

    delete element 4

    After performing the binary search deletion, the RB Tree property is not disturbed, therefore the tree is left as it is.

    Then, we delete the element 5 using the binary search deletion

    delete element 5

    But the RB property is violated after performing the binary search deletion, i.e., all the paths in the tree do not hold same number of black nodes; so we swap the colors to balance the tree.

    RB property violated

    Then, we delete the node 3 from the tree obtained −

    Applying binary search deletion, we delete node 3 normally as it is a leaf node. And we get a double node as 3 is a black colored node.

    delete node 3

    We apply case 3 deletion as double black’s sibling node is black and its child nodes are also black. Here, we remove the double black, recolor the double black’s parent and sibling.

    RB Tree property maintained

    All the desired nodes are deleted and the RB Tree property is maintained.

    The search operation in red-black tree follows the same algorithm as that of a binary search tree. The tree is traversed and each node is compared with the key element to be searched; if found it returns a successful search. Otherwise, it returns an unsuccessful search.

    Complete implementation

    Following are the complete implementations of Red Black Tree in various programming languages −

    C++JavaPython

    // C++ program for Red black trees algorithmn#include <iostream>usingnamespace std;structNode{int data;
      Node *parent;
      Node *left;
      Node *right;int color;};typedef Node *NodePtr;classRedBlackTree{private:
      NodePtr root;
      NodePtr TNULL;voidinitializeNULLNode(NodePtr node, NodePtr parent){
        node->data =0;
        node->parent = parent;
        node->left =nullptr;
        node->right =nullptr;
        node->color =0;}// PreordervoidpreOrderHelper(NodePtr node){if(node != TNULL){
          cout << node->data <<" ";preOrderHelper(node->left);preOrderHelper(node->right);}}// InordervoidinOrderHelper(NodePtr node){if(node != TNULL){inOrderHelper(node->left);
          cout << node->data <<" ";inOrderHelper(node->right);}}// Post ordervoidpostOrderHelper(NodePtr node){if(node != TNULL){postOrderHelper(node->left);postOrderHelper(node->right);
          cout << node->data <<" ";}}
      NodePtr searchTreeHelper(NodePtr node,int key){if(node == TNULL || key == node->data){return node;}if(key < node->data){returnsearchTreeHelper(node->left, key);}returnsearchTreeHelper(node->right, key);}// For balancing the tree after deletionvoiddeleteFix(NodePtr x){
        NodePtr s;while(x != root && x->color ==0){if(x == x->parent->left){
            s = x->parent->right;if(s->color ==1){
              s->color =0;
              x->parent->color =1;leftRotate(x->parent);
              s = x->parent->right;}if(s->left->color ==0&& s->right->color ==0){
              s->color =1;
              x = x->parent;}else{if(s->right->color ==0){
                s->left->color =0;
                s->color =1;rightRotate(s);
                s = x->parent->right;}
              s->color = x->parent->color;
              x->parent->color =0;
              s->right->color =0;leftRotate(x->parent);
              x = root;}}else{
            s = x->parent->left;if(s->color ==1){
              s->color =0;
              x->parent->color =1;rightRotate(x->parent);
              s = x->parent->left;}if(s->right->color ==0&& s->right->color ==0){
              s->color =1;
              x = x->parent;}else{if(s->left->color ==0){
                s->right->color =0;
                s->color =1;leftRotate(s);
                s = x->parent->left;}
              s->color = x->parent->color;
              x->parent->color =0;
              s->left->color =0;rightRotate(x->parent);
              x = root;}}}
        x->color =0;}voidrbTransplant(NodePtr u, NodePtr v){if(u->parent ==nullptr){
          root = v;}elseif(u == u->parent->left){
          u->parent->left = v;}else{
          u->parent->right = v;}
        v->parent = u->parent;}voiddeleteNodeHelper(NodePtr node,int key){
        NodePtr z = TNULL;
        NodePtr x, y;while(node != TNULL){if(node->data == key){
            z = node;}if(node->data <= key){
            node = node->right;}else{
            node = node->left;}}if(z == TNULL){
          cout <<"Key not found in the tree"<< endl;return;}
        y = z;int y_original_color = y->color;if(z->left == TNULL){
          x = z->right;rbTransplant(z, z->right);}elseif(z->right == TNULL){
          x = z->left;rbTransplant(z, z->left);}else{
          y =minimum(z->right);
          y_original_color = y->color;
          x = y->right;if(y->parent == z){
            x->parent = y;}else{rbTransplant(y, y->right);
            y->right = z->right;
            y->right->parent = y;}rbTransplant(z, y);
          y->left = z->left;
          y->left->parent = y;
          y->color = z->color;}delete z;if(y_original_color ==0){deleteFix(x);}}// For balancing the tree after insertionvoidinsertFix(NodePtr k){
        NodePtr u;while(k->parent->color ==1){if(k->parent == k->parent->parent->right){
            u = k->parent->parent->left;if(u->color ==1){
              u->color =0;
              k->parent->color =0;
              k->parent->parent->color =1;
              k = k->parent->parent;}else{if(k == k->parent->left){
                k = k->parent;rightRotate(k);}
              k->parent->color =0;
              k->parent->parent->color =1;leftRotate(k->parent->parent);}}else{
            u = k->parent->parent->right;if(u->color ==1){
              u->color =0;
              k->parent->color =0;
              k->parent->parent->color =1;
              k = k->parent->parent;}else{if(k == k->parent->right){
                k = k->parent;leftRotate(k);}
              k->parent->color =0;
              k->parent->parent->color =1;rightRotate(k->parent->parent);}}if(k == root){break;}}
        root->color =0;}voidprintHelper(NodePtr root, string indent,bool last){if(root != TNULL){
          cout << indent;if(last){
            cout <<"R----";
            indent +="   ";}else{
            cout <<"L----";
            indent +="|  ";}
          string sColor = root->color ?"RED":"BLACK";
          cout << root->data <<"("<< sColor <<")"<< endl;printHelper(root->left, indent,false);printHelper(root->right, indent,true);}}public:RedBlackTree(){
        TNULL =new Node;
        TNULL->color =0;
        TNULL->left =nullptr;
        TNULL->right =nullptr;
        root = TNULL;}voidpreorder(){preOrderHelper(this->root);}voidinorder(){inOrderHelper(this->root);}voidpostorder(){postOrderHelper(this->root);}
      NodePtr searchTree(int k){returnsearchTreeHelper(this->root, k);}
      NodePtr minimum(NodePtr node){while(node->left != TNULL){
          node = node->left;}return node;}
      NodePtr maximum(NodePtr node){while(node->right != TNULL){
          node = node->right;}return node;}
      NodePtr successor(NodePtr x){if(x->right != TNULL){returnminimum(x->right);}
        NodePtr y = x->parent;while(y != TNULL && x == y->right){
          x = y;
          y = y->parent;}return y;}
      NodePtr predecessor(NodePtr x){if(x->left != TNULL){returnmaximum(x->left);}
        NodePtr y = x->parent;while(y != TNULL && x == y->left){
          x = y;
          y = y->parent;}return y;}voidleftRotate(NodePtr x){
        NodePtr y = x->right;
        x->right = y->left;if(y->left != TNULL){
          y->left->parent = x;}
        y->parent = x->parent;if(x->parent ==nullptr){this->root = y;}elseif(x == x->parent->left){
          x->parent->left = y;}else{
          x->parent->right = y;}
        y->left = x;
        x->parent = y;}voidrightRotate(NodePtr x){
        NodePtr y = x->left;
        x->left = y->right;if(y->right != TNULL){
          y->right->parent = x;}
        y->parent = x->parent;if(x->parent ==nullptr){this->root = y;}elseif(x == x->parent->right){
          x->parent->right = y;}else{
          x->parent->left = y;}
        y->right = x;
        x->parent = y;}// Inserting a nodevoidinsert(int key){
        NodePtr node =new Node;
        node->parent =nullptr;
        node->data = key;
        node->left = TNULL;
        node->right = TNULL;
        node->color =1;
        NodePtr y =nullptr;
        NodePtr x =this->root;while(x != TNULL){
          y = x;if(node->data < x->data){
            x = x->left;}else{
            x = x->right;}}
        node->parent = y;if(y ==nullptr){
          root = node;}elseif(node->data < y->data){
          y->left = node;}else{
          y->right = node;}if(node->parent ==nullptr){
          node->color =0;return;}if(node->parent->parent ==nullptr){return;}insertFix(node);}
      NodePtr getRoot(){returnthis->root;}voiddeleteNode(int data){deleteNodeHelper(this->root, data);}voidprintTree(){if(root){printHelper(this->root,"",true);}}};intmain(){
      RedBlackTree V;
        V.insert(24);
        V.insert(33);
        V.insert(42);
        V.insert(51);
        V.insert(60);
        V.insert(40);
        V.insert(22);
      V.printTree();
      cout << endl
         <<"After deleting an element"<< endl;
      V.deleteNode(40);
      V.printTree();}

    Output

    R----33(BLACK)
       L----24(BLACK)
       |  L----22(RED)
       R----51(RED)
          L----42(BLACK)
          |  L----40(RED)
          R----60(BLACK)
    
    After deleting an element
    R----33(BLACK)
       L----24(BLACK)
       |  L----22(RED)
       R----51(RED)
          L----42(BLACK)
          R----60(BLACK)
  • AVL Trees

    The first type of self-balancing binary search tree to be invented is the AVL tree. The name AVL tree is coined after its inventor’s names − Adelson-Velsky and Landis.

    In AVL trees, the difference between the heights of left and right subtrees, known as the Balance Factor, must be at most one. Once the difference exceeds one, the tree automatically executes the balancing algorithm until the difference becomes one again.

    BALANCE FACTOR = 
    HEIGHT(LEFT SUBTREE) − HEIGHT(RIGHT SUBTREE)
    

    There are usually four cases of rotation in the balancing algorithm of AVL trees: LL, RR, LR, RL.

    LL Rotations

    LL rotation is performed when the node is inserted into the right subtree leading to an unbalanced tree. This is a single left rotation to make the tree balanced again −

    LL Rotations

    Fig : LL Rotation

    The node where the unbalance occurs becomes the left child and the newly added node becomes the right child with the middle node as the parent node.

    RR Rotations

    RR rotation is performed when the node is inserted into the left subtree leading to an unbalanced tree. This is a single right rotation to make the tree balanced again −

    RR_Rotations

    Fig : RR Rotation

    The node where the unbalance occurs becomes the right child and the newly added node becomes the left child with the middle node as the parent node.

    LR Rotations

    LR rotation is the extended version of the previous single rotations, also called a double rotation. It is performed when a node is inserted into the right subtree of the left subtree. The LR rotation is a combination of the left rotation followed by the right rotation. There are multiple steps to be followed to carry this out.

    • Consider an example with A as the root node, B as the left child of A and C as the right child of B.
    • Since the unbalance occurs at A, a left rotation is applied on the child nodes of A, i.e. B and C.
    • After the rotation, the C node becomes the left child of A and B becomes the left child of C.
    • The unbalance still persists, therefore a right rotation is applied at the root node A and the left child C.
    • After the final right rotation, C becomes the root node, A becomes the right child and B is the left child.
    LR_Rotation

    Fig : LR Rotation

    RL Rotations

    RL rotation is also the extended version of the previous single rotations, hence it is called a double rotation and it is performed if a node is inserted into the left subtree of the right subtree. The RL rotation is a combination of the right rotation followed by the left rotation. There are multiple steps to be followed to carry this out.

    • Consider an example with A as the root node, B as the right child of A and C as the left child of B.
    • Since the unbalance occurs at A, a right rotation is applied on the child nodes of A, i.e. B and C.
    • After the rotation, the C node becomes the right child of A and B becomes the right child of C.
    • The unbalance still persists, therefore a left rotation is applied at the root node A and the right child C.
    • After the final left rotation, C becomes the root node, A becomes the left child and B is the right child.
    RL Rotations

    Fig : RL Rotation

    Basic Operations of AVL Trees

    The basic operations performed on the AVL Tree structures include all the operations performed on a binary search tree, since the AVL Tree at its core is actually just a binary search tree holding all its properties. Therefore, basic operations performed on an AVL Tree are − Insertion and Deletion.

    Insertion operation

    The data is inserted into the AVL Tree by following the Binary Search Tree property of insertion, i.e. the left subtree must contain elements less than the root value and right subtree must contain all the greater elements.

    However, in AVL Trees, after the insertion of each element, the balance factor of the tree is checked; if it does not exceed 1, the tree is left as it is. But if the balance factor exceeds 1, a balancing algorithm is applied to readjust the tree such that balance factor becomes less than or equal to 1 again.Algorithm

    The following steps are involved in performing the insertion operation of an AVL Tree −

    Step 1 − Create a node
    Step 2 − Check if the tree is empty
    Step 3 − If the tree is empty, the new node created will become the 
             root node of the AVL Tree.
    Step 4 − If the tree is not empty, we perform the Binary Search Tree 
             insertion operation and check the balancing factor of the node 
             in the tree.
    Step 5 − Suppose the balancing factor exceeds 1, we apply suitable 
             rotations on the said node and resume the insertion from Step 4.
    

    Let us understand the insertion operation by constructing an example AVL tree with 1 to 7 integers.

    Starting with the first element 1, we create a node and measure the balance, i.e., 0.

    AVL1

    Since both the binary search property and the balance factor are satisfied, we insert another element into the tree.

    AVL2

    The balance factor for the two nodes are calculated and is found to be -1 (Height of left subtree is 0 and height of the right subtree is 1). Since it does not exceed 1, we add another element to the tree.

    3rd element

    Now, after adding the third element, the balance factor exceeds 1 and becomes 2. Therefore, rotations are applied. In this case, the RR rotation is applied since the imbalance occurs at two right nodes.

    RR rotation applied

    The tree is rearranged as −

    tree rearranged

    Similarly, the next elements are inserted and rearranged using these rotations. After rearrangement, we achieve the tree as −

    balance

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structNode{int data;structNode*leftChild;structNode*rightChild;int height;};intmax(int a,int b);intheight(structNode*N){if(N ==NULL)return0;return N->height;}intmax(int a,int b){return(a > b)? a : b;}structNode*newNode(int data){structNode*node =(structNode*)malloc(sizeof(structNode));
       node->data = data;
       node->leftChild =NULL;
       node->rightChild =NULL;
       node->height =1;return(node);}structNode*rightRotate(structNode*y){structNode*x = y->leftChild;structNode*T2 = x->rightChild;
       x->rightChild = y;
       y->leftChild = T2;
       y->height =max(height(y->leftChild),height(y->rightChild))+1;
       x->height =max(height(x->leftChild),height(x->rightChild))+1;return x;}structNode*leftRotate(structNode*x){structNode*y = x->rightChild;structNode*T2 = y->leftChild;
       y->leftChild = x;
       x->rightChild = T2;
       x->height =max(height(x->leftChild),height(x->rightChild))+1;
       y->height =max(height(y->leftChild),height(y->rightChild))+1;return y;}intgetBalance(structNode*N){if(N ==NULL)return0;returnheight(N->leftChild)-height(N->rightChild);}structNode*insertNode(structNode*node,int data){if(node ==NULL)return(newNode(data));if(data < node->data)
          node->leftChild =insertNode(node->leftChild, data);elseif(data > node->data)
          node->rightChild =insertNode(node->rightChild, data);elsereturn node;
       node->height =1+max(height(node->leftChild),height(node->rightChild));int balance =getBalance(node);if(balance >1&& data < node->leftChild->data)returnrightRotate(node);if(balance <-1&& data > node->rightChild->data)returnleftRotate(node);if(balance >1&& data > node->leftChild->data){
          node->leftChild =leftRotate(node->leftChild);returnrightRotate(node);}if(balance <-1&& data < node->rightChild->data){
          node->rightChild =rightRotate(node->rightChild);returnleftRotate(node);}return node;}structNode*minValueNode(structNode*node){structNode*current = node;while(current->leftChild !=NULL)
          current = current->leftChild;return current;}voidprintTree(structNode*root){if(root ==NULL)return;if(root !=NULL){printTree(root->leftChild);printf("%d ", root->data);printTree(root->rightChild);}}intmain(){structNode*root =NULL;
       root =insertNode(root,22);
       root =insertNode(root,14);
       root =insertNode(root,72);
       root =insertNode(root,44);
       root =insertNode(root,25);
       root =insertNode(root,63);
       root =insertNode(root,98);printf("AVL Tree: ");printTree(root);return0;}

    Output

    AVL Tree: 14 22 25 44 63 72 98 
    

    Deletion operation

    Deletion in the AVL Trees take place in three different scenarios −

    • Scenario 1 (Deletion of a leaf node) − If the node to be deleted is a leaf node, then it is deleted without any replacement as it does not disturb the binary search tree property. However, the balance factor may get disturbed, so rotations are applied to restore it.
    • Scenario 2 (Deletion of a node with one child) − If the node to be deleted has one child, replace the value in that node with the value in its child node. Then delete the child node. If the balance factor is disturbed, rotations are applied.
    • Scenario 3 (Deletion of a node with two child nodes) − If the node to be deleted has two child nodes, find the inorder successor of that node and replace its value with the inorder successor value. Then try to delete the inorder successor node. If the balance factor exceeds 1 after deletion, apply balance algorithms.

    Using the same tree given above, let us perform deletion in three scenarios −

    balance
    • Deleting element 7 from the tree above −

    Since the element 7 is a leaf, we normally remove the element without disturbing any other node in the tree

    remove 7th element
    • Deleting element 6 from the output tree achieved −

    However, element 6 is not a leaf node and has one child node attached to it. In this case, we replace node 6 with its child node: node 5.

    replace node

    The balance of the tree becomes 1, and since it does not exceed 1 the tree is left as it is. If we delete the element 5 further, we would have to apply the left rotations; either LL or LR since the imbalance occurs at both 1-2-4 and 3-2-4.

    balance2

    The balance factor is disturbed after deleting the element 5, therefore we apply LL rotation (we can also apply the LR rotation here).

    apply LR rotation

    Once the LL rotation is applied on path 1-2-4, the node 3 remains as it was supposed to be the right child of node 2 (which is now occupied by node 4). Hence, the node is added to the right subtree of the node 2 and as the left child of the node 4.

    balance minus one
    • Deleting element 2 from the remaining tree −

    As mentioned in scenario 3, this node has two children. Therefore, we find its inorder successor that is a leaf node (say, 3) and replace its value with the inorder successor.

    balance zero

    The balance of the tree still remains 1, therefore we leave the tree as it is without performing any rotations.Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structNode{int data;structNode*leftChild;structNode*rightChild;int height;};intmax(int a,int b);intheight(structNode*N){if(N ==NULL)return0;return N->height;}intmax(int a,int b){return(a > b)? a : b;}structNode*newNode(int data){structNode*node =(structNode*)malloc(sizeof(structNode));
       node->data = data;
       node->leftChild =NULL;
       node->rightChild =NULL;
       node->height =1;return(node);}structNode*rightRotate(structNode*y){structNode*x = y->leftChild;structNode*T2 = x->rightChild;
       x->rightChild = y;
       y->leftChild = T2;
       y->height =max(height(y->leftChild),height(y->rightChild))+1;
       x->height =max(height(x->leftChild),height(x->rightChild))+1;return x;}structNode*leftRotate(structNode*x){structNode*y = x->rightChild;structNode*T2 = y->leftChild;
       y->leftChild = x;
       x->rightChild = T2;
       x->height =max(height(x->leftChild),height(x->rightChild))+1;
       y->height =max(height(y->leftChild),height(y->rightChild))+1;return y;}intgetBalance(structNode*N){if(N ==NULL)return0;returnheight(N->leftChild)-height(N->rightChild);}structNode*insertNode(structNode*node,int data){if(node ==NULL)return(newNode(data));if(data < node->data)
          node->leftChild =insertNode(node->leftChild, data);elseif(data > node->data)
          node->rightChild =insertNode(node->rightChild, data);elsereturn node;
       node->height =1+max(height(node->leftChild),height(node->rightChild));int balance =getBalance(node);if(balance >1&& data < node->leftChild->data)returnrightRotate(node);if(balance <-1&& data > node->rightChild->data)returnleftRotate(node);if(balance >1&& data > node->leftChild->data){
          node->leftChild =leftRotate(node->leftChild);returnrightRotate(node);}if(balance <-1&& data < node->rightChild->data){
          node->rightChild =rightRotate(node->rightChild);returnleftRotate(node);}return node;}structNode*minValueNode(structNode*node){structNode*current = node;while(current->leftChild !=NULL)
          current = current->leftChild;return current;}structNode*deleteNode(structNode*root,int data){if(root ==NULL)return root;if(data < root->data)
          root->leftChild =deleteNode(root->leftChild, data);elseif(data > root->data)
          root->rightChild =deleteNode(root->rightChild, data);else{if((root->leftChild ==NULL)||(root->rightChild ==NULL)){structNode*temp = root->leftChild ? root->leftChild : root->rightChild;if(temp ==NULL){
                temp = root;
                root =NULL;}else*root =*temp;free(temp);}else{structNode*temp =minValueNode(root->rightChild);
             root->data = temp->data;
             root->rightChild =deleteNode(root->rightChild, temp->data);}}if(root ==NULL)return root;
       root->height =1+max(height(root->leftChild),height(root->rightChild));int balance =getBalance(root);if(balance >1&&getBalance(root->leftChild)>=0)returnrightRotate(root);if(balance >1&&getBalance(root->leftChild)<0){
          root->leftChild =leftRotate(root->leftChild);returnrightRotate(root);}if(balance <-1&&getBalance(root->rightChild)<=0)returnleftRotate(root);if(balance <-1&&getBalance(root->rightChild)>0){
          root->rightChild =rightRotate(root->rightChild);returnleftRotate(root);}return root;}// Print the treevoidprintTree(structNode*root){if(root !=NULL){printTree(root->leftChild);printf("%d ", root->data);printTree(root->rightChild);}}intmain(){structNode*root =NULL;
       root =insertNode(root,22);
       root =insertNode(root,14);
       root =insertNode(root,72);
       root =insertNode(root,44);
       root =insertNode(root,25);
       root =insertNode(root,63);
       root =insertNode(root,98);printf("AVL Tree: ");printTree(root);
       root =deleteNode(root,25);printf("\nAfter deletion: ");printTree(root);return0;}

    Output

    AVL Tree: 14 22 25 44 63 72 98 
    After deletion: 14 22 44 63 72 98 
  • Binary Search Tree

    A Binary Search Tree (BST) is a tree in which all the nodes follow the below-mentioned properties −

    • The left sub-tree of a node has a key less than or equal to its parent node’s key.
    • The right sub-tree of a node has a key greater than or equal to its parent node’s key.

    Thus, BST divides all its sub-trees into two segments; the left sub-tree and the right sub-tree and can be defined as −

    left_subtree (keys) &leq; node (key) &leq; right_subtree (keys)
    

    Binary Tree Representation

    BST is a collection of nodes arranged in a way where they maintain BST properties. Each node has a key and an associated value. While searching, the desired key is compared to the keys in BST and if found, the associated value is retrieved.

    Following is a pictorial representation of BST −

    Tree Traversal

    We observe that the root node key (27) has all less-valued keys on the left sub-tree and the higher valued keys on the right sub-tree.

    Basic Operations

    Following are the basic operations of a Binary Search Tree −

    • Search − Searches an element in a tree.
    • Insert − Inserts an element in a tree.
    • Pre-order Traversal − Traverses a tree in a pre-order manner.
    • In-order Traversal − Traverses a tree in an in-order manner.
    • Post-order Traversal − Traverses a tree in a post-order manner.

    Defining a Node

    Define a node that stores some data, and references to its left and right child nodes.

    struct node {
       int data;
       struct node *leftChild;
       struct node *rightChild;
    };
    

    Whenever an element is to be searched, start searching from the root node. Then if the data is less than the key value, search for the element in the left subtree. Otherwise, search for the element in the right subtree. Follow the same algorithm for each node.

    Algorithm

    1. START
    2. Check whether the tree is empty or not
    3. If the tree is empty, search is not possible
    4. Otherwise, first search the root of the tree.
    5. If the key does not match with the value in the root, 
       search its subtrees.
    6. If the value of the key is less than the root value, 
       search the left subtree
    7. If the value of the key is greater than the root value, 
       search the right subtree.
    8. If the key is not found in the tree, return unsuccessful search.
    9. END
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild,*rightChild;};structnode*root =NULL;structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->data = item;
       temp->leftChild = temp->rightChild =NULL;return temp;}voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}structnode*search(int data){structnode*current = root;while(current->data != data){//go to left treeif(current->data > data){
                current = current->leftChild;}//else go to right treeelse{
                current = current->rightChild;}//not foundif(current ==NULL){returnNULL;}}return current;}voidprintTree(structnode* Node){if(Node ==NULL)return;printTree(Node->leftChild);printf(" --%d", Node->data);printTree(Node->rightChild);}intmain(){insert(55);insert(20);insert(90);insert(50);insert(35);insert(15);insert(65);printf("Insertion done");printf("\nBST: \n");printTree(root);structnode* k;int ele =35;printf("\nElement to be searched: %d", ele);
       k =search(35);if(k !=NULL)printf("\nElement %d found", k->data);elseprintf("\nElement not found");return0;}

    Output

    Insertion done
    BST: 
     --15 --20 --35 --50 --55 --65 --90
    Element to be searched: 35
    Element 35 found
    

    Insertion Operation

    Whenever an element is to be inserted, first locate its proper location. Start searching from the root node, then if the data is less than the key value, search for the empty location in the left subtree and insert the data. Otherwise, search for the empty location in the right subtree and insert the data.

    Algorithm

    1. START
    2. If the tree is empty, insert the first element as the root node of the 
       tree. The following elements are added as the leaf nodes.
    3. If an element is less than the root value, it is added into the left 
       subtree as a leaf node.
    4. If an element is greater than the root value, it is added into the right 
       subtree as a leaf node.
    5. The final leaf nodes of the tree point to NULL values as their 
       child nodes.
    6. END
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild,*rightChild;};structnode*root =NULL;structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->data = item;
       temp->leftChild = temp->rightChild =NULL;return temp;}voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}voidprintTree(structnode* Node){if(Node ==NULL)return;printTree(Node->leftChild);printf(" --%d", Node->data);printTree(Node->rightChild);}intmain(){insert(55);insert(20);insert(90);insert(50);insert(35);insert(15);insert(65);printf("Insertion done\n");printf("BST: \n");printTree(root);return0;}

    Output

    Insertion done
    BST: 
     --15 --20 --35 --50 --55 --65 --90
    

    Inorder Traversal

    The inorder traversal operation in a Binary Search Tree visits all its nodes in the following order −

    • Firstly, we traverse the left child of the root node/current node, if any.
    • Next, traverse the current node.
    • Lastly, traverse the right child of the current node, if any.

    Algorithm

    1. START
    2. Traverse the left subtree, recursively
    3. Then, traverse the root node
    4. Traverse the right subtree, recursively.
    5. END
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int key;structnode*left,*right;};structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->key = item;
       temp->left = temp->right =NULL;return temp;}// Inorder Traversalvoidinorder(structnode*root){if(root !=NULL){inorder(root->left);printf("%d -> ", root->key);inorder(root->right);}}// Insertion operationstructnode*insert(structnode*node,int key){if(node ==NULL)returnnewNode(key);if(key < node->key)
          node->left =insert(node->left, key);else
          node->right =insert(node->right, key);return node;}intmain(){structnode*root =NULL;
       root =insert(root,55);
       root =insert(root,20);
       root =insert(root,90);
       root =insert(root,50);
       root =insert(root,35);
       root =insert(root,15);
       root =insert(root,65);printf("Inorder traversal: ");inorder(root);}

    Output

    Inorder traversal: 15 -> 20 -> 35 -> 50 -> 55 -> 65 -> 90 -> 
    

    Preorder Traversal

    The preorder traversal operation in a Binary Search Tree visits all its nodes. However, the root node in it is first printed, followed by its left subtree and then its right subtree.

    Algorithm

    1. START
    2. Traverse the root node first.
    3. Then traverse the left subtree, recursively
    4. Later, traverse the right subtree, recursively.
    5. END
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int key;structnode*left,*right;};structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->key = item;
       temp->left = temp->right =NULL;return temp;}// Preorder Traversalvoidpreorder(structnode*root){if(root !=NULL){printf("%d -> ", root->key);preorder(root->left);preorder(root->right);}}// Insertion operationstructnode*insert(structnode*node,int key){if(node ==NULL)returnnewNode(key);if(key < node->key)
          node->left =insert(node->left, key);else
          node->right =insert(node->right, key);return node;}intmain(){structnode*root =NULL;
       root =insert(root,55);
       root =insert(root,20);
       root =insert(root,90);
       root =insert(root,50);
       root =insert(root,35);
       root =insert(root,15);
       root =insert(root,65);printf("Preorder traversal: ");preorder(root);}

    Output

    Preorder traversal: 55 -> 20 -> 15 -> 50 -> 35 -> 90 -> 65 -> 
    

    Postorder Traversal

    Like the other traversals, postorder traversal also visits all the nodes in a Binary Search Tree and displays them. However, the left subtree is printed first, followed by the right subtree and lastly, the root node.

    Algorithm

    1. START
    2. Traverse the left subtree, recursively
    3. Traverse the right subtree, recursively.
    4. Then, traverse the root node
    5. END
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int key;structnode*left,*right;};structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->key = item;
       temp->left = temp->right =NULL;return temp;}// Postorder Traversalvoidpostorder(structnode*root){if(root !=NULL){printf("%d -> ", root->key);postorder(root->left);postorder(root->right);}}// Insertion operationstructnode*insert(structnode*node,int key){if(node ==NULL)returnnewNode(key);if(key < node->key)
          node->left =insert(node->left, key);else
          node->right =insert(node->right, key);return node;}intmain(){structnode*root =NULL;
       root =insert(root,55);
       root =insert(root,20);
       root =insert(root,90);
       root =insert(root,50);
       root =insert(root,35);
       root =insert(root,15);
       root =insert(root,65);printf("Postorder traversal: ");postorder(root);}

    Output

    Postorder traversal: 55 -> 20 -> 15 -> 50 -> 35 -> 90 > 65 -> 
    

    Complete implementation

    Following are the complete implementations of Binary Search Tree in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild,*rightChild;};structnode*root =NULL;structnode*newNode(int item){structnode*temp =(structnode*)malloc(sizeof(structnode));
       temp->data = item;
       temp->leftChild = temp->rightChild =NULL;return temp;}voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}structnode*search(int data){structnode*current = root;while(current->data != data){if(current !=NULL){//go to left treeif(current->data > data){
                current = current->leftChild;}//else go to right treeelse{
                current = current->rightChild;}//not foundif(current ==NULL){returnNULL;}}}return current;}// Inorder Traversalvoidinorder(structnode*root){if(root !=NULL){inorder(root->leftChild);printf("%d -> ", root->data);inorder(root->rightChild);}}// Preorder Traversalvoidpreorder(structnode*root){if(root !=NULL){printf("%d -> ", root->data);preorder(root->leftChild);preorder(root->rightChild);}}// Postorder Traversalvoidpostorder(structnode*root){if(root !=NULL){printf("%d -> ", root->data);postorder(root->leftChild);postorder(root->rightChild);}}intmain(){insert(55);insert(20);insert(90);insert(50);insert(35);insert(15);insert(65);printf("Insertion done");printf("\nPreorder Traversal: ");preorder(root);printf("\nInorder Traversal: ");inorder(root);printf("\nPostorder Traversal: ");postorder(root);structnode* k;int ele =35;printf("\nElement to be searched: %d", ele);
       k =search(35);if(k !=NULL)printf("\nElement %d found", k->data);elseprintf("\nElement not found");return0;}

    Output

    Insertion done
    Preorder Traversal: 55 -> 20 -> 15 -> 50 -> 35 -> 90 -> 65 -> 
    Inorder Traversal: 15 -> 20 -> 35 -> 50 -> 55 -> 65 -> 90 -> 
    Postorder Traversal: 55 -> 20 -> 15 -> 50 -> 35 -> 90 -> 65 -> 
    Element to be searched: 35
    Element 35 found
  • Tree Traversal

    Traversal is a process to visit all the nodes of a tree and may print their values too. Because, all nodes are connected via edges (links) we always start from the root (head) node. That is, we cannot randomly access a node in a tree. There are three ways which we use to traverse a tree −

    • In-order Traversal
    • Pre-order Traversal
    • Post-order Traversal

    Generally, we traverse a tree to search or locate a given item or key in the tree or to print all the values it contains.

    In-order Traversal

    In this traversal method, the left subtree is visited first, then the root and later the right sub-tree. We should always remember that every node may represent a subtree itself.

    If a binary tree is traversed in-order, the output will produce sorted key values in an ascending order.

    In-order Traversal

    We start from A, and following in-order traversal, we move to its left subtree B.B is also traversed in-order. The process goes on until all the nodes are visited. The output of in-order traversal of this tree will be −

    D B E A F C G

    Algorithm

    Until all nodes are traversed −

    Step 1 − Recursively traverse left subtree.
    Step 2 − Visit root node.
    Step 3 − Recursively traverse right subtree.
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild;structnode*rightChild;};structnode*root =NULL;voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}voidinorder_traversal(structnode* root){if(root !=NULL){inorder_traversal(root->leftChild);printf("%d ",root->data);inorder_traversal(root->rightChild);}}intmain(){int i;int array[7]={27,14,35,10,19,31,42};for(i =0; i <7; i++)insert(array[i]);printf("Inorder traversal: ");inorder_traversal(root);return0;}

    Output

    Inorder traversal: 10 14 19 27 31 35 42
    

    Pre-order Traversal

    In this traversal method, the root node is visited first, then the left subtree and finally the right subtree.

    Pre-order Traversal

    We start from A, and following pre-order traversal, we first visit A itself and then move to its left subtree BB is also traversed pre-order. The process goes on until all the nodes are visited. The output of pre-order traversal of this tree will be −

    A → B → D → E → C → F → G

    Algorithm

    Until all nodes are traversed −

    Step 1 − Visit root node.
    Step 2 − Recursively traverse left subtree.
    Step 3 − Recursively traverse right subtree.
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild;structnode*rightChild;};structnode*root =NULL;voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}voidpre_order_traversal(structnode* root){if(root !=NULL){printf("%d ",root->data);pre_order_traversal(root->leftChild);pre_order_traversal(root->rightChild);}}intmain(){int i;int array[7]={27,14,35,10,19,31,42};for(i =0; i <7; i++)insert(array[i]);printf("Preorder traversal: ");pre_order_traversal(root);return0;}

    Output

    Preorder traversal: 27 14 10 19 35 31 42 
    

    Post-order Traversal

    In this traversal method, the root node is visited last, hence the name. First we traverse the left subtree, then the right subtree and finally the root node.

    Post-order Traversal

    We start from A, and following pre-order traversal, we first visit the left subtree BB is also traversed post-order. The process goes on until all the nodes are visited. The output of post-order traversal of this tree will be

    D → E → B → F → G → C → A

    Algorithm

    Until all nodes are traversed −

    Step 1 − Recursively traverse left subtree.
    Step 2 − Recursively traverse right subtree.
    Step 3 − Visit root node.
    

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild;structnode*rightChild;};structnode*root =NULL;voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}voidpost_order_traversal(structnode* root){if(root !=NULL){post_order_traversal(root->leftChild);post_order_traversal(root->rightChild);printf("%d ", root->data);}}intmain(){int i;int array[7]={27,14,35,10,19,31,42};for(i =0; i <7; i++)insert(array[i]);printf("Post order traversal: ");post_order_traversal(root);return0;}

    Output

    Post order traversal: 10 19 14 31 42 35 27
    

    Complete Implementation

    Now let’s see the complete implementation of tree traversal in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structnode{int data;structnode*leftChild;structnode*rightChild;};structnode*root =NULL;voidinsert(int data){structnode*tempNode =(structnode*)malloc(sizeof(structnode));structnode*current;structnode*parent;
       tempNode->data = data;
       tempNode->leftChild =NULL;
       tempNode->rightChild =NULL;//if tree is emptyif(root ==NULL){
          root = tempNode;}else{
          current = root;
          parent =NULL;while(1){
             parent = current;//go to left of the treeif(data < parent->data){
                current = current->leftChild;//insert to the leftif(current ==NULL){
                   parent->leftChild = tempNode;return;}}//go to right of the treeelse{
                current = current->rightChild;//insert to the rightif(current ==NULL){
                   parent->rightChild = tempNode;return;}}}}}voidpre_order_traversal(structnode* root){if(root !=NULL){printf("%d ",root->data);pre_order_traversal(root->leftChild);pre_order_traversal(root->rightChild);}}voidinorder_traversal(structnode* root){if(root !=NULL){inorder_traversal(root->leftChild);printf("%d ",root->data);inorder_traversal(root->rightChild);}}voidpost_order_traversal(structnode* root){if(root !=NULL){post_order_traversal(root->leftChild);post_order_traversal(root->rightChild);printf("%d ", root->data);}}intmain(){int i;int array[7]={27,14,35,10,19,31,42};for(i =0; i <7; i++)insert(array[i]);printf("Preorder traversal: ");pre_order_traversal(root);printf("\nInorder traversal: ");inorder_traversal(root);printf("\nPost order traversal: ");post_order_traversal(root);return0;}

    Output

    Preorder traversal: 27 14 10 19 35 31 42 
    Inorder traversal: 10 14 19 27 31 35 42 
    Post order traversal: 10 19 14 31 42 35 27
  • Tree Data Structure

    Tree Data Structrue

    A tree is a non-linear abstract data type with a hierarchy-based structure. It consists of nodes (where the data is stored) that are connected via links. The tree data structure stems from a single node called a root node and has subtrees connected to the root.

    Tree Data Structure

    Important Terms

    Following are the important terms with respect to tree.

    • Path − Path refers to the sequence of nodes along the edges of a tree.
    • Root − The node at the top of the tree is called root. There is only one root per tree and one path from the root node to any node.
    • Parent − Any node except the root node has one edge upward to a node called parent.
    • Child − The node below a given node connected by its edge downward is called its child node.
    • Leaf − The node which does not have any child node is called the leaf node.
    • Subtree − Subtree represents the descendants of a node.
    • Visiting − Visiting refers to checking the value of a node when control is on the node.
    • Traversing − Traversing means passing through nodes in a specific order.
    • Levels − Level of a node represents the generation of a node. If the root node is at level 0, then its next child node is at level 1, its grandchild is at level 2, and so on.
    • Keys − Key represents a value of a node based on which a search operation is to be carried out for a node.

    Types of Trees

    There are three types of trees −

    • General Trees
    • Binary Trees
    • Binary Search Trees

    General Trees

    General trees are unordered tree data structures where the root node has minimum 0 or maximum n subtrees.

    The General trees have no constraint placed on their hierarchy. The root node thus acts like the superset of all the other subtrees.

    General Trees

    Binary Trees

    Binary Trees are general trees in which the root node can only hold up to maximum 2 subtrees: left subtree and right subtree. Based on the number of children, binary trees are divided into three types.

    Full Binary Tree

    • A full binary tree is a binary tree type where every node has either 0 or 2 child nodes.

    Complete Binary Tree

    • A complete binary tree is a binary tree type where all the leaf nodes must be on the same level. However, root and internal nodes in a complete binary tree can either have 0, 1 or 2 child nodes.

    Perfect Binary Tree

    • A perfect binary tree is a binary tree type where all the leaf nodes are on the same level and every node except leaf nodes have 2 children.
    Binary Trees

    Binary Search Trees

    Binary Search Trees possess all the properties of Binary Trees including some extra properties of their own, based on some constraints, making them more efficient than binary trees.

    The data in the Binary Search Trees (BST) is always stored in such a way that the values in the left subtree are always less than the values in the root node and the values in the right subtree are always greater than the values in the root node, i.e. left subtree < root node right subtree.

    binary serach tree

    Advantages of BST

    • Binary Search Trees are more efficient than Binary Trees since time complexity for performing various operations reduces.
    • Since the order of keys is based on just the parent node, searching operation becomes simpler.
    • The alignment of BST also favors Range Queries, which are executed to find values existing between two keys. This helps in the Database Management System.

    Disadvantages of BST

    The main disadvantage of Binary Search Trees is that if all elements in nodes are either greater than or lesser than the root node, the tree becomes skewed. Simply put, the tree becomes slanted to one side completely.

    This skewness will make the tree a linked list rather than a BST, since the worst case time complexity for searching operation becomes O(n).

    To overcome this issue of skewness in the Binary Search Trees, the concept of Balanced Binary Search Trees was introduced.

    Balanced Binary Search Trees

    Consider a Binary Search Tree with m as the height of the left subtree and n as the height of the right subtree. If the value of (m-n) is equal to 0,1 or -1, the tree is said to be a Balanced Binary Search Tree.

    The trees are designed in a way that they self-balance once the height difference exceeds 1. Binary Search Trees use rotations as self-balancing algorithms. There are four different types of rotations: Left Left, Right Right, Left Right, Right Left.

    There are various types of self-balancing binary search trees −