简体   繁体   English

二进制搜索树在C-错误中删除具有左右子树的节点

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

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! 即,如果您将值23,14,31,7,9输入树并删除23,则出来的值为14 14 7 9.请帮忙!

#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;
}

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; nodeToRemove->键= nodeLargestLeft->键;

You try to delete 23 from your example tree 您尝试从示例树中删除23

      23
     /  \
   14    31
  /
 7
  \ 
   9

which has 14 as largest value in the left subtree and now looks like this: 在左子树中有14个最大值,现在看起来像这样:

      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). 相反,您必须为重复节点(第二级中的节点14)执行常规删除过程。 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 当删除仅具有左子树或右子树的根节点时, parentNULL -pointer,导致parent- 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. findParent的调用看起来很难看,在遍历树或为每个节点维护父指针时更好地跟踪父节点。

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;
            }
       }   

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 DELETION是b-tree中最不正常的例程,这是我的,这是维基文章,它恢复我所做的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;
        }

    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM