在c中的二进制搜索树中执行删除操作

时间:2016-02-02 15:58:43

标签: c binary-search-tree

我编写了一个程序,它将两个文件名作为参数,f1和f2,两个文件都带有数字。 程序应该可以调用如下:tree f1 f2

f1拥有数百万个唯一数字,每行一个。应读取每个数字并将其插入二进制搜索树

插入这些文件后,程序应从第二个文件中读取数字。对于每个号码,必须完成以下操作:

  • 在树中搜索号码
  • 如果存在,请询问用户并从树中删除

现在,我的插入和搜索代码正在给出正确的结果,但是,在删除的部分,有一些错误。

请修改我的代码来帮助我:

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

struct node {
    int info;
    struct node *left, *right;
};

struct node *insert (struct node *root, int item)
{
    struct node *temp, *temp1, *pre;

    temp = (struct node *) malloc (sizeof (struct node));
    temp->info = item;
    temp->left = temp->right = NULL;

    if (root == NULL)
        root = temp;
    else {
        temp1 = root;
        while (temp1 != NULL) {
            pre = temp1;
            if (item < temp1->info)
                temp1 = temp1->left;
            else
                temp1 = temp1->right;

        }
        if (item < pre->info)
            pre->left = temp;
        else
            pre->right = temp;
    }

    return root;
}

struct node *create (struct node *root)
{
    int num;
    root = NULL;
    FILE *fp1 = fopen ("myFile1.txt", "r");
    if (fp1 == NULL) {
        printf ("cannot open this file");
        exit (0);
    }

    while (fscanf (fp1, "%d", &num) == 1)
        root = insert (root, num);

    return root;
    fclose (fp1);   /* (note: unreachable code) */
}

struct node *min (struct node *ptr)
{
    struct node *current = ptr;

    while (current->left != NULL)
        current = current->left;

    return current;
}

struct node *delete (struct node *root, int n)
{
    if (root == NULL)
        return root;

    if (n < root->info)
        root->left = delete (root->left, n);
    else if (n > root->info)
        root->right = delete (root->right, n);
    else {
        if (root->left == NULL) {
            struct node *p;
            p = root->right;
            free (root);
            return p;
        }
        else if (root->right == NULL) {
            struct node *p;
            p = root->left;
            free (root);
            return p;
        }

        struct node *p;
        p = min (root->right);
        root->info = p->info;
        root->right = delete (root->right, p->info);
    }

    return root;
}

void search (struct node *root)
{
    int Y, X;
    struct node *t;
    t = root;
    char ch = 'n';
    FILE *fp2 = fopen ("myFile2.txt", "r");
    if (fp2 == NULL) {
        printf ("cannot open this file");
        exit (0);
    }
    X = 0;
    while (fscanf (fp2, "%d", &Y) == 1) {
        while (t != NULL && X == 0) {
            if (Y == t->info) {
                X = 1;
                break;
            } else if (Y < t->info)
                t = t->left;
            else
                t = t->right;
        }

        if (X == 1)
            printf (" %d is found %d\n", Y, X);
        printf ("if you want to delete a number ");
        scanf ("%c", &ch);
        if (ch == 'y') {
            root = delete (root, Y);
            return root;

        }
        else
            printf ("%dNot found %d\n", Y, X);

    }
    fclose (fp2);
}

void inorder (struct node *root)
{
    if (root != NULL) {
        inorder (root->left);
        printf ("%d ", root->info);
        inorder (root->right);
    }
}

void main ()
{
    struct node *root = NULL;
    struct node *ptr = NULL;
    root = create (root);
    inorder (root);
    search (root);
    inorder (root);
}

2 个答案:

答案 0 :(得分:0)

以下是更正后的代码:

#include<stdio.h>
#include<stdlib.h>
struct node
{
 int info;
 struct node *left, *right;
};
struct node* insert(struct node* root, int item)
{
 struct node *temp,*temp1,*pre;
 temp =  (struct node *)malloc(sizeof(struct node));
 temp->info = item;
 temp->left = temp->right = NULL;

 if (root == NULL)
 {
    root=temp;
 }
 else
 {
   temp1=root;
   while(temp1!=NULL)
   {
           pre=temp1;
           if(item<temp1->info)
           {
               temp1=temp1->left;
           }
           else
           {
               temp1=temp1->right;
           }
   }
   if(item<pre->info)
   {
      pre->left=temp;
   }
   else
   {
      pre->right=temp;
   }
 }
 return root;
}

struct node *create(struct node *root)
{
   int num;
   root=NULL;
   FILE *fp1=fopen("myFile1.txt","r");
   if (fp1 == NULL)
   {
        printf("cannot open this file");
        exit(0);
   }
   while(fscanf(fp1,"%d",&num)==1)
       root=insert(root,num);
   fclose(fp1);
   return root;
}
struct node * min(struct node* ptr)
{
        struct node* current = ptr;
        while (current->left != NULL)
        current = current->left;

        return current;
}
struct node* delete(struct node* root,int n)
{
   if(root==NULL)
   {
       return root;
   }
   if(n<root->info)
   {
       root->left=delete(root->left,n);
   }
   else if(n>root->info)
   {
       root->right=delete(root->right,n);
   }
   else
   {
          if(root->left==NULL)
          {
               struct node *p;
               p=root->right;
               free(root);
               return p;
          }
          else
          if(root->right==NULL)
          {
                struct node *p;
                p=root->left;
                free(root);
                return p;
          }

          struct node *p;
          p=min(root->right);
          root->info=p->info;
          root->right=delete(root->right,p->info);
   }
   return root;
}

void search(struct node *root)
{
      int Y,X;
      struct node *t;
      char ch='n';
      FILE *fp2=fopen("myFile2.txt","r");
      if (fp2 == NULL)
      {
          printf("cannot open this file");
          exit(0);
      }
      while(fscanf(fp2,"%d",&Y)==1)
      {
            t = root;
            X = 0;
            while(t!=NULL && X==0)
            {
               if(Y==t->info)
               {
                     X=1;
                     break;
               }
               else
               {
                 if(Y<t->info)
                 {
                     t=t->left;
                 }
                 else
                 {
                     t=t->right;
                 }
               }
            }

            if(X==1)
            {
              printf("\n%d is found\n",Y);
              printf("\nDo you want to delete %d (y/n): ",Y);
              scanf(" %c",&ch);
              if(ch=='y')
              {
                 root=delete(root,Y);
              }
            }
            else
            {
                printf("\n%d Not found %d\n",Y,X);
            }
      }
      fclose(fp2);
 }

 void inorder(struct node *root)
 {
          if (root != NULL)
          {
               inorder(root->left);
               printf("%d ", root->info);
               inorder(root->right);
          }
 }

 void main()
 {
       struct node *root = NULL;
       struct node *ptr = NULL;
       root=create(root);
       inorder(root);
       printf("\n");
       search(root);
       printf("\n");
       inorder(root);
       printf("\n");
 }

除了格式的微小更改外,以下是主要更改:

  • 始终记得为每个控制语句(if,while,else等)添加{}大括号,即使它有一个操作,因为稍后可能会添加代码对于相同的控制语句,忘记大括号意味着控制语句不会应用于该代码。对于例如你忘记了括号:

    if(X==1)
     printf(" %d is found %d\n",Y,X);
     printf("if you want to delete a number ");
     scanf("%c",&ch);
     if(ch=='y')
     {
       root=delete(root,Y);
       return root;
     }
     else
      printf("%dNot found %d\n",Y,X);
    

    现在由于缺少大括号,其他内容正在应用于if(ch=='y')而不是if(X==1)

  • 其次,无论何时将条件变量初始化为值并对其进行检查,都在嵌套循环内,并且对于多个输入或案例重复操作,初始化也需要在内部完成循环。在t = root;函数中X = 0;search的情况也是如此,它们只是设置了一次,当从文件中读取下一个数字时,它们没有再次初始化。

  • 您在return root;函数中调用void search(struct node *root),其返回类型为void,这意味着它返回

    if(ch=='y')
    {
     root=delete(root,Y);
     return root;
    }
    

    并且,return调用意味着该函数将停止进一步执行,然后在那里,return调用之下的剩余代码将不会被执行,这不是什么你想要的,如:

    struct node *create(struct node *root)
    {
     int num;
     ...
     while(fscanf(fp1,"%d",&num)==1)
      root=insert(root,num);
     return root;
     fclose(fp1);
    }
    

    这里,fclose(fp1);永远不会被调用。

  • 最后但并非最不重要的是,更好的代码缩进可以帮助理解每个控制语句的范围所在,而不必跟踪每个左括号{到其结束{{1} }}

答案 1 :(得分:-1)

从长远来看,您还有其他几个方面可以避免给自己造成问题。您希望避免代码主体中的硬编码文件名,更不用说硬编码隐藏在函数中的文件名。如果您需要对某个文件进行操作,例如createsearch,请将FILE*参数(或文件名)传递给该函数。这将使您的代码更易于维护和重用。对于create,你会做类似的事情:

struct node *create (struct node *root, FILE *fp)
{
    int num;
    root = NULL;

    while (fscanf (fp, "%d", &num) == 1)
        root = insert (root, num);

    return root;
}

对于您当前的search,您可以执行以下操作:

void search (struct node *root, FILE *fp)
{
    int v;

    while (fscanf (fp, "%d", &v) == 1) {

        struct node *t = root;
        char ch = 'n';

        while (t != NULL) {
            if (t->info == v) {
                printf ("\n%d is found\n", v);
                printf ("\nDo you want to delete %d (y/n): ", v);
                scanf (" %c", &ch);
                if (ch == 'y') {
                    root = delete (root, v);
                }
                break;
            } 
            else {
                if (v < t->info)
                    t = t->left;
                else
                    t = t->right;
            }
        }
        if (!t)
            printf ("\n%d Not found\n", v);
    }
}

但为什么要将FILE *指针传递给search开始? search不应只搜索您的树并返回指向包含匹配值(如果存在)的节点的指针,否则返回NULL?这肯定会使search在您的树的许多不同情况下使用一般函数,而不是对第二个文件中的匹配值进行一次性检查。

如何获取代码所需的文件名信息?就像您将所需参数传递给您在代码中调用的每个函数一样,您可以以相同的方式将所需信息传递给mainmain接受命令行中给出的参数并将它们传递给您的程序。 main的一般形式是:

int main (int argc, char **argv)

(您还会看到等效形式int main (int argc, char *argv[]),它只是反映argv的替代形式,而不显示数组作为函数传递时自动发生的数组转换结果参数)

使用参数获取main的信息而不是硬编码。这并不意味着您不能在代码中提供默认文件名,如果没有提供参数,将使用这些文件名。您可以使用三元运算符,它基本上是if-else形式的简写(condition) ? true_value : false_value语句。有了它,你可以从命令行获取文件名,同时仍然提供在没有提供参数的情况下使用的文件名的默认值(注意:第一个参数将始终用作fp1)。 e.g:

int main (int argc, char **argv)
{
    FILE *fp1 = fopen (argc > 1 ? argv[1] : "myfile1.txt", "r");
    FILE *fp2 = fopen (argc > 2 ? argv[2] : "myfile2.txt", "r");
    if (fp1 == NULL) {
        fprintf (stderr, "cannot open fp1\n");
        return 1;
    }
    if (fp2 == NULL) {
        fprintf (stderr, "cannot open fp2\n");
        return 1;
    }

然后,您可以将FILE*指针(fp1fp2)传递给任何需要它们的函数,例如:

root = create (root, fp1);

createsearch删除了硬编码文件名后,您可以将注意力转移到创建search,这比仅检查第二个文件中的匹配值更有用。重写search,它只需要一个指向root的指针和要搜索的值,然后它可以返回指示是否在树中找到该值(通常是指向包含该节点的节点的指针)是否值,返回NULL指针。例如:

struct node *search (struct node *root, int v)
{
    struct node *t = root;

    while (t != NULL) {
        if (t->info == v)
            return t;
        else {
            if (v < t->info)
                t = t->left;
            else
                t = t->right;
        }
    }
    return t;
}

要以可维护的方式完成代码,您只需编写一个函数来读取第二个文件search树的值,然后在找到值时提示允许删除。同样,它只需要作为参数指向rootFILE*指针来读取值。然后它可以从那里处理调用searchdelete。 (注意:delete可以被重写以直接对search返回的指针进行操作,以避免第二次遍历,但这是另一天)。例如,您的检查和提示删除功能可能是:

void check_delete (struct node *root, FILE *fp)
{
    int v;

    while (fscanf (fp, "%d", &v) == 1) {

        char ch = 'n';

        if (search (root, v)) {

            printf ("\n%d is found\n", v);
            printf ("\nDo you want to delete %d (y/n): ", v);
            scanf (" %c", &ch);

            if (ch == 'y')
                root = delete (root, v);
        }
        else
            printf ("\n%d Not found\n", v);
    }
}

将拼图的所有部分放在一起,并在main上方提供函数原型(最终将其与结构一起移动到正确的头文件中)和函数定义(最终将移动到一个单独的源文件),您将结束类似于以下代码的内容。请注意,这并不是一个包含所有建议的更改或改进的详尽重写,但重要的是以正确的方式开始处理代码所需的信息,并避免尽早陷入不良习惯。如果您还有其他问题,请与我们联系:

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

struct node {
    int info;
    struct node *left, *right;
};

/* function prototypes */
struct node *insert (struct node *root, int item);
struct node *create (struct node *root, FILE *fp);
struct node *min (struct node *ptr);
struct node *delete (struct node *root, int n);
struct node *search (struct node *root, int v);
void check_delete (struct node *root, FILE *fp);
void inorder (struct node *root);

int main (int argc, char **argv)
{
    FILE *fp1 = fopen (argc > 1 ? argv[1] : "myfile1.txt", "r");
    FILE *fp2 = fopen (argc > 2 ? argv[2] : "myfile2.txt", "r");
    if (fp1 == NULL) {
        fprintf (stderr, "cannot open fp1\n");
        return 1;
    }
    if (fp2 == NULL) {
        fprintf (stderr, "cannot open fp2\n");
        return 1;
    }
    struct node *root = NULL;

    root = create (root, fp1);
    fclose (fp1);

    inorder (root);
    putchar ('\n');

    check_delete (root, fp2);
    fclose (fp2);
    putchar ('\n');

    inorder (root);
    putchar ('\n');

    return 0;
}

struct node *insert (struct node *root, int item)
{
    struct node *temp, *temp1, *pre;

    temp = malloc (sizeof *temp);
    temp->info = item;
    temp->left = temp->right = NULL;

    if (root == NULL) {
        root = temp;
    } 
    else {
        temp1 = root;

        while (temp1 != NULL) {
            pre = temp1;
            if (item < temp1->info)
                temp1 = temp1->left;
            else
                temp1 = temp1->right;
        }

        if (item < pre->info)
            pre->left = temp;
        else
            pre->right = temp;
    }

    return root;
}

struct node *create (struct node *root, FILE *fp)
{
    int num;
    root = NULL;

    while (fscanf (fp, "%d", &num) == 1)
        root = insert (root, num);

    return root;
}

struct node *min (struct node *ptr)
{
    struct node *current = ptr;
    while (current->left != NULL)
        current = current->left;

    return current;
}

struct node *delete (struct node *root, int n)
{
    if (root == NULL) {
        return root;
    }
    if (n < root->info) {
        root->left = delete (root->left, n);
    } else if (n > root->info) {
        root->right = delete (root->right, n);
    } else {
        if (root->left == NULL) {
            struct node *p;
            p = root->right;
            free (root);
            return p;
        } else if (root->right == NULL) {
            struct node *p;
            p = root->left;
            free (root);
            return p;
        }

        struct node *p;
        p = min (root->right);
        root->info = p->info;
        root->right = delete (root->right, p->info);
    }
    return root;
}

struct node *search (struct node *root, int v)
{
    struct node *t = root;

    while (t != NULL) {
        if (t->info == v)
            return t;
        else {
            if (v < t->info)
                t = t->left;
            else
                t = t->right;
        }
    }
    return (t);
}

void check_delete (struct node *root, FILE *fp)
{
    int v;

    while (fscanf (fp, "%d", &v) == 1) {

        // struct node *t = root;
        char ch = 'n';

        if (search (root, v)) {

            printf ("\n%d is found\n", v);
            printf ("\nDo you want to delete %d (y/n): ", v);
            scanf (" %c", &ch);

            if (ch == 'y')
                root = delete (root, v);
        }
        else
            printf ("\n%d Not found\n", v);
    }
}

void inorder (struct node *root)
{
    if (root != NULL) {
        inorder (root->left);
        printf ("%d ", root->info);
        inorder (root->right);
    }
}