Binary Search Tree in C- error in deleting node with both left and right subtree

Question

I'm almost finished with my Binary Search Tree program. However, I'm stuck at deletion: removing a node with both left and right subtrees. The largest left value is promoted in the left subtree. It sometimes works, but does not always work the way it should be. Ie, if you input values 23, 14, 31, 7, 9 into the tree and remove 23, the values that come out are 14 14 7 9. Please help!

#include <stdio.h>
#include <stdlib.h>


typedef struct node { 
    int key; 
    struct node* left;
    struct node* right;
}node;

struct node* root= NULL;
int count=0;
void preOrder(node* temp){

    if (temp!=NULL){
        printf("%d ",temp->key);
        preOrder(temp->left);
        preOrder(temp->right);
    }
}
//for printing
void inOrder(node* temp){

        if (temp!=NULL){
           inOrder(temp->left);
           printf("%d ",temp->key);
           inOrder(temp->right);
        }

}
void printPrompt(void){
    int choice=-1;

    do{
        printf("   Enter <1> Inorder <2> Preorder <3> Return to Menu: ");
        scanf("%d", &choice);

        if(choice!=1 && choice!=2 && choice!=3)  printf("   Error: invalid input! \n\n");
        if(choice==1){
           if(root==NULL) printf("\tError: is empty tree\n");
              else {
                   printf("\tInorder:\t ");
                   inOrder(root);
                   printf("\n\n");
              }
        }
        else if (choice==2){
           struct node* temp=root;
           if(root==NULL) printf("\tError: is empty tree\n");
              else {
                   printf("\tPreorder:\t ");
                   preOrder(root);
                   printf("\n\n");
              }
        }


    }while (choice!=3);
    printf("   <Exit print method>\n\n");

}
//printing complete

//both are similar code- one searches and another finds the node
int contains(node* current, int value){
    if(current==NULL) return 0;
    if (value==current->key) {
        return 1;
    }
    else if(value < current->key) return contains(current->left, value);
    else return contains(current->right, value);
}
node* findParent(node* current, int value){
    if (value == current->key) return NULL; //if only one node in BST, then no parent node
    if (value < current->key) {
        if (current->left == NULL) return NULL; //if value not found, return null
        else if (current->left->key == value) return current;
        else return findParent (current->left, value);
    }
    else {
        if (current->right == NULL) return NULL;
        else if (current->right->key== value) return current;
        else return findParent (current->right, value);
    }
}

node* findNode(node* current, int value){
    if (current == NULL) return NULL;
    if (current->key == value) {
        return current;
    }
    else if (value < current->key)  return findNode (current->left, value);
    else return findNode (current->right, value);
}
//

void del(value){
    struct node* nodeToRemove= findNode(root, value);
    if (nodeToRemove == NULL) printf("\tError: node not found in tree\n");
    else {
       struct node* parent = findParent(root, value);
       if (count == 1 ) {
            root= NULL; 
            printf("\tRemoving the only node in BST\n");
       }
       else if (nodeToRemove->left == NULL && nodeToRemove->right == NULL){
            printf("\tRemoving leaf node in BST\n");
            if (nodeToRemove->key < parent->key) parent->left = NULL;
            else parent->right = NULL;
       }
       else if (nodeToRemove->left== NULL && nodeToRemove->right != NULL){
            printf("\tRemoving node with right subtree but no left subtree\n");
            if (nodeToRemove->key < parent->key) {
                parent->left = nodeToRemove->right;
            }
            else parent->right = nodeToRemove->right;
       }
       else if (nodeToRemove->left!= NULL && nodeToRemove->right == NULL){
            printf("\tRemoving node with left subtree but no right subtree\n");
            if (nodeToRemove->key < parent->key) {
                parent->left = nodeToRemove->left;
            }
            else parent->right = nodeToRemove->left;
       }
       else{
            printf("\tRemoving node with both left subtree and right subtree\n");
            struct node* nodeLargestLeft = nodeToRemove -> left;
                while (nodeLargestLeft -> right != NULL) nodeLargestLeft= nodeLargestLeft->right;
            findParent(root, nodeLargestLeft->key)->right=NULL;
            nodeToRemove->key=nodeLargestLeft->key;
       }   
   }

            printf("\tResult: ");
            preOrder(root);
            printf("\n");  
            count= count-1;
}

void deletePrompt(void){
    int value=-1;
    do{
        printf("   Delete key or press -1 to return to menu:  ");
        scanf("%d", &value);
        if(value>0){
           if(root==NULL) printf("\tError: is empty tree\n");
             else del(value);

        }
         else if (value<=0) printf("\tError: key not positive\n");
    }while (value!=-1); 
    printf("   <Exit delete method>\n\n");
}

void searchPrompt(void){
    int value=-1;
    do{
        printf("   Search key or press -1 to return to menu: ");
        scanf("%d", &value);
        if(value>0){
            if (root==NULL) printf("\tError: tree is empty\n"); 
            else {
                if(contains(root, value)) printf("\tKey %d is found\n",value);
                else printf("\tKey %d is not found\n",value);
            }
        }
        else if (value<=0) printf("\tError: key not positive\n");
    }while (value!=-1);
    printf("   <Exit search method>\n\n");
}
//for search




//for insertion
void insertNode(node* current, int value){        
        if(value< current->key){
            if (current->left == NULL) {
                current->left=(node*) malloc(sizeof(node));
                current->left->key = value;
                current->left->left = NULL;
                current->left->right = NULL;
                printf("\tSuccess! Value inserted: %d\n", current->left->key);

            }
            else {
                insertNode(current->left, value);
            }
        }
        else {
            if (current->right == NULL) {
                current->right=(node*) malloc(sizeof(node));
                current->right->key = value;
                current->right->left = NULL;
                current->right->right = NULL;
                printf("\tSuccess! Value inserted: %d\n", current->right->key);
            }
            else {
                insertNode(current->right, value);
            }
        }
}//end insert

void insert(int value){

    if(root==NULL){  //empty tree
        root =(node*) malloc(sizeof(node));

        root->key= value;
        printf("\tPrint root here: %d\n", value);
        root->left= NULL;
        root->right=NULL;
        printf("\tSuccess! Value inserted: %d\n", root->key);
    }
    else {
        insertNode(root, value);
    }        
        printf("\tResult: ");
        preOrder(root);
        printf("\n");
}

void insertPrompt(void){
    int value=-1;
    do{
        printf("   Insert value or press -1 to return to menu:  ");
        scanf("%d", &value);
        if(value>0){
            insert(value);
            count= count+1;
            printf("\tCount: %d\n", count);
        }
        else if (value<=0)printf("\tError: key not positive\n");
    }while (value!=-1);
    printf("   <Exit insert method>\n\n");

}

int menuPrompt(void){
    int choice=-1;
    do{
        printf("Enter <1> Search <2> Insert <3> Delete <4> Print Tree <5> Quit: ");
        scanf("%d", &choice);
        if(choice>5 || choice<1) printf("Error: invalid input! \n\n");
    }  while(choice>5 || choice<1);
    return choice;

}


int main(int argc, char *argv[]){
   int choice=-1;
   int value=-1;


    while(choice!=5){

   choice=menuPrompt();

   switch(choice){
    case 1:
         searchPrompt();
         break;
    case 2:
         insertPrompt();
         break;
    case 3:
         deletePrompt();
         break;
    case 4:
         printPrompt();
         break;    
    case 5:
         printf("<Exit program> \n");
         break;
   }//end switch

}

  system("PAUSE");  
  return 0;
}

Show source
| C   | binary   | search   | tree   2017-01-02 08:01 3 Answers

Answers to Binary Search Tree in C- error in deleting node with both left and right subtree ( 3 )

  1. 2017-01-02 11:01

    Your deletion algorithm for a node with two subtrees is slightly wrong. What you are correctly doing is:

    Find the maximum of the left subtree (or minimum of the right subtree):

    struct node* nodeLargestLeft = nodeToRemove -> left;
    while (nodeLargestLeft -> right != NULL)
        nodeLargestLeft= nodeLargestLeft->right;
    
    • Replace the value of the node to be removed with the value of the node found above

      nodeToRemove->key=nodeLargestLeft->key;

    You try to delete 23 from your example tree

          23
         /  \
       14    31
      /
     7
      \ 
       9
    

    which has 14 as largest value in the left subtree and now looks like this:

          14
         /  \
       14    31
      /
     7
      \ 
       9
    

    But now, you can't just delete the the right subtree of the parent of the largest node (which is your root node). This would be the right subtree of your whole binary tree in the example case you mention!

    findParent(root, nodeLargestLeft->key)->right=NULL; // wrong
    

    Instead you have to do a regular deletion procedure for the duplicate node (the node 14 in the second level). Since it was the node with the largest value in the left subtree it cannot have a right subtree. So there are only two cases: Either, the left subtree is empty too, in which case the node can be discarded, or it is populated, in which case the root node of the left subtree replaces this node.

    In your example the second case occurs and your tree should then look like that:

          14
         /  \
        7    31
         \ 
          9
    

    With minimal intrusive changes this procedure should work:

    printf("\tRemoving node with both left subtree and right subtree\n");
    struct node* nodeLargestLeft = nodeToRemove->left;
    parent = findParent(root, nodeLargestLeft->key);
    while (nodeLargestLeft -> right != NULL) {
        parent = nodeLargestLeft;
        nodeLargestLeft= nodeLargestLeft->right;
    }
    nodeToRemove->key=nodeLargestLeft->key;
    parent->left = nodeLargestLeft->left;
    

    Your code has several other issues, for example

    • when deleting the root node with only a left or a right subtree, parent is a NULL-pointer, leading to a segfault on parent->key
    • duplicates are inconsistently handled
    • many code paths can be merged to a single one, eliminating redundancy and enhancing code readability
    • the calls to findParent look ugly, better track the parent while traversing the tree or maintaining a parent-pointer for each node.
  2. 2017-01-02 11:01

    Okay I solved my own problem. There was a special case.

     else{
                printf("\tRemoving node with both left subtree and right subtree\n");
                //special case, if left of nodeToRemove has no right node
                struct node* nodeLargestLeft = nodeToRemove -> left;
                if (nodeToRemove->left->right == NULL) {
                    nodeToRemove->key = nodeToRemove->left ->key;
                    nodeToRemove->left = nodeToRemove->left->left;
    
                }else{
                    while (nodeLargestLeft -> right != NULL) nodeLargestLeft= nodeLargestLeft->right;
                    findParent(root, nodeLargestLeft->key)->right=NULL;
                    nodeToRemove->key=nodeLargestLeft->key;
                }
           }   
    
  3. 2017-01-02 13:01

    DELETION is the most perverse routine in a b-tree this is mine, and this is the wiki article that resume what I did https://en.wikipedia.org/wiki/Binary_search_tree#Deletion

    void  bt_rec_delete(pbbtree bt, size_t root)
    {
        ptbBtreeNode me, right, left, parent, replacer;
        char side;
        ll rec;
        me = &bt->tb[root];
        me->deleted = TRUE;
        right = me->right == 0 ? NULL : &bt->tb[me->right];
        left = me->left == 0 ? NULL : &bt->tb[me->left];
        parent = me->parent == 0 ? NULL : &bt->tb[me->parent];
        //     if (parent)
        //          disp_dbt(bt,parent->index,0);
        if (parent)
            side = parent->right == root ? 'r' : 'l';
        else
            side = 0;
    
        if (!right && !left)
        {
            if (side == 'r')
                parent->right = 0;
            else if (side == 'l')
                parent->left = 0;
            else
                bt->current_root = 0;
            propagate_depth(bt, root);
        }
        else if (!right)
        {
            left->parent = me->parent;
            if (side == 'r')
                parent->right = left->index;
            else if (side == 'l')
                parent->left = left->index;
            else
                bt->current_root = left->index;
            propagate_depth(bt, left->index);
        }
        else if (!left)
        {
            right->parent = me->parent;
            if (side == 'r')
                parent->right = right->index;
            else if (side == 'l')
                parent->left = right->index;
            else
                bt->current_root = right->index;
            propagate_depth(bt, right->index);
        }
        else
        {
            unsigned rec_par;
            if (right->depth > left->depth)
            {
                rec = bt_get_maximum(bt, right->index);
                assert(rec > 0);
                rec_par = bt->tb[rec].parent;
    
                if (rec_par != me->index)
                {
                    bt->tb[rec_par].left = bt->tb[rec].right; // maximum assure me there is no left leaf
                    if (bt->tb[rec].right > 0)
                        bt->tb[bt->tb[rec].right].parent = rec_par;
                    bt->tb[rec].right = 0;
                }
                propagate_depth(bt, rec_par);
            }
            else
            {
                rec = bt_get_minimum(bt, left->index);
                assert(rec > 0);
                rec_par = bt->tb[rec].parent;
    
                if (rec_par != me->index)
                {
                    bt->tb[rec_par].right = bt->tb[rec].left;// minimum assure me there is no right leaf
                    if (bt->tb[rec].left > 0)
                        bt->tb[bt->tb[rec].left].parent = rec_par;
                    bt->tb[rec].left = 0;
                }
    
                propagate_depth(bt, rec_par);
            }
            replacer = &bt->tb[rec];
            replacer->depth = me->depth;
            if (side == 'r')
                parent->right = replacer->index;
            else if (side == 'l')
                parent->left = replacer->index;
            else
                bt->current_root = replacer->index;
            replacer->parent = me->parent;
            if (replacer->index != left->index)
            {
                replacer->left = left->index;
                left->parent = replacer->index;
            }
            if (replacer->index != right->index)
            {
                replacer->right = right->index;
                right->parent = replacer->index;
            }
    
        }
    
    }
    

Leave a reply to - Binary Search Tree in C- error in deleting node with both left and right subtree

◀ Go back