2017-01-02 108 views
3

我差不多完成了我的二叉搜索樹程序。但是,我堅持刪除:使用左右子樹移除節點。最左側的值在左側子樹中進行提升。它有時會起作用,但並不總是按照它的方式工作。也就是說,如果您將值23,14,31,7,9輸入到樹中並刪除23,那麼出現的值是14 14 7 9.請幫忙!二進制搜索樹在C-錯誤中刪除節點與左右子樹

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

回答

3

對於具有兩個子樹的節點,您的刪除算法有點不對。你正在做正確的是:

查找(右子樹或最小值)最大左子樹的:

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; 

你的代碼有幾個其他的問題,例如

  • 當刪除只有左或右子樹的根節點時,parentNULL終場,導致段錯誤的parent->key
  • 重複的不一致處理
  • 很多的代碼路徑可以合併到一個單一的一個,消除冗餘,增強代碼的可讀性
  • findParent來電長得不好看,在遍歷樹的同時更好地跟蹤父節點或爲每個節點維護父指針。
0

好吧我解決了我自己的問題。有一個特例。

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

刪除是在B樹,這是我的, 最有害的程序,這是簡歷就是我所做的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; 
     } 

    } 

} 
維基文章