【问题标题】:Implementation of Delete operation in a Binary Search Tree in cc中二叉搜索树中删除操作的实现
【发布时间】:2016-02-02 15:58:43
【问题描述】:

我编写了一个程序,它将两个文件名作为参数,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);
}

【问题讨论】:

  • 一些错误?错误是什么?
  • 你用调试器单步调试了吗?
  • 您有机会查看答案吗?

标签: c binary-search-tree


【解决方案1】:

以下是更正后的代码:

#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等)加上{}大括号,即使它只有一个操作,因为以后可能会将代码添加到同一个control statement 和忘记的大括号意味着 control statement 不会应用于该代码。例如你忘记了大括号:

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

    现在由于缺少大括号,else 被应用于if(ch=='y') 而不是if(X==1)

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

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

    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); 永远不会被调用。

  • 1234563 /p>

【讨论】:

    【解决方案2】:

    从长远来看,您还希望避免在其他几个方面给自己造成问题。您希望避免在代码主体中硬编码文件名,更不用说埋在函数中的硬编码文件名了。如果你需要对一个文件进行操作,比如你做create或者search,就给函数传递一个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 的信息,而不是硬编码。这并不意味着你不能在你的代码中提供 default 文件名,如果没有提供参数将被使用。您可以使用 三元 运算符,它基本上是 (condition) ? true_value : false_value 形式的简写 if-else 语句。有了它,您可以从命令行获取文件名,同时仍然为在没有提供参数的情况下要使用的文件名提供默认值(注意:第一个参数将始终用作fp1)。例如:

    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 您的树中读取值,然后在找到该值时提示允许删除。同样,它只需要一个指向root 的指针和一个用于从中读取值的FILE* 指针作为参数。然后它可以处理从那里调用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);
        }
    }
    

    【讨论】:

    • 作者要求更正他/她的代码中关于删除的错误,这是一些就地更改,而不是修改整个代码。虽然,您给出了很好的建议,但它们不仅仅是解决作者的问题,这可能会给后来的观众或作者造成混淆。
    • 你是谁?我不得不整天纠正你草率的编辑。你担心你的答案会被不必要的变量弄得乱七八糟,我会担心我的——交易?
    • 这不是仇恨游戏。我只是表达了我的想法。你在说什么“草率的编辑”和“充满不必要变量的答案”?我不明白你。
    • 在我的编辑中,我只是修正了语法并保持原样,而您重新缩进了代码。两次修改都是出于不同的目的,都是必不可少的。
    • 可能存在语言障碍。草率的编辑是您重新格式化帖子中的代码,在括号之间留下多个空行等。如果您要费心清理别人的代码,至少要使其简洁易读,以便下一个人可能乐于助人。您对search 的更正离开了X,除了复制t 的测试告诉您开始的内容之外没有其他目的,并且大写 的保留在很大程度上与公认的C 相违背风格。现在我们都说了我们的观点,让我们继续吧。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    相关资源
    最近更新 更多