【问题标题】:Binary Search Tree in C- error in deleting node with both left and right subtreeC中的二叉搜索树-删除具有左右子树的节点时出错
【发布时间】:2017-01-02 07:33:50
【问题描述】:

我几乎完成了我的二叉搜索树程序。但是,我坚持删除:删除具有左右子树的节点。在左子树中提升最大的左值。它有时有效,但并不总是按应有的方式工作。即,如果将值 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;
}

【问题讨论】:

    标签: c binary-search-tree


    【解决方案1】:

    您对具有两个子树的节点的删除算法略有错误。你正确的做法是:

    求左子树的最大值(或右子树的最小值):

    struct node* nodeLargestLeft = nodeToRemove -> left;
    while (nodeLargestLeft -> right != NULL)
        nodeLargestLeft= nodeLargestLeft->right;
    
    • 将要移除的节点的值替换为上面找到的节点的值

      nodeToRemove->key=nodeLargestLeft->key;

    您尝试从示例树中删除 23

          23
         /  \
       14    31
      /
     7
      \ 
       9
    

    左子树中的最大值为 14,现在看起来像这样:

          14
         /  \
       14    31
      /
     7
      \ 
       9
    

    但是现在,您不能只删除最大节点(即您的根节点)的父节点的右子树。在您提到的示例中,这将是整个二叉树的右子树!

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

    相反,您必须对重复节点(第二级中的节点 14)执行定期删除过程。由于它是左子树中具有最大值的节点,因此它不能有右子树。所以只有两种情况:要么左子树也是空的,这种情况下该节点可以被丢弃,或者它被填充,此时左子树的根节点替换该节点。

    在您的示例中,发生了第二种情况,您的树应如下所示:

          14
         /  \
        7    31
         \ 
          9
    

    此过程只需进行最少的侵入性更改即可:

    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;
    

    您的代码还有其他几个问题,例如

    • 当删除只有左子树或右子树的根节点时,parent 是一个NULL 指针,导致parent-&gt;key 出现段错误
    • 重复处理不一致
    • 许多代码路径可以合并为一个,消除冗余并增强代码可读性
    • findParent 的调用看起来很难看,在遍历树或为每个节点维护父指针时更好地跟踪父节点。

    【讨论】:

      【解决方案2】:

      好的,我解决了我自己的问题。有一个特殊情况。

       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】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多