繁体   English   中英

二进制搜索树节点插入无法正常工作

[英]Binary Search Tree node insertion not working properly

我有一个作业分配来实现一个二进制搜索树(创建,删除,搜索)。 我使用了老师提供的示例,但无法使其正常工作。

到目前为止,这是我的代码:

void insert_node(int k){
    struct node *nodnou,*flow,*parent;

    nodnou = (struct node*)malloc(sizeof(node));
    nodnou->st = NULL;
    nodnou->dr = NULL;
    nodnou->nr = k;

    if (root==NULL)
    {
        root = (struct node*)malloc(sizeof(node));

        root = nodnou;
    }
    else
    {
        flow = (struct node*)malloc(sizeof(node));
        parent = (struct node*)malloc(sizeof(node));
        flow = root;
        parent = root;
        while (((flow->st!=NULL) || (flow->dr!=NULL)) && flow!=NULL)
        {
            if (k<flow->nr)
            {
                parent = flow;
                flow = flow->st;
            }
            else
            {
                parent = flow;
                flow = flow->dr;
            }
        }

        if (k<flow->nr)
        {
            parent->st = nodnou;
        }
        else
        {
            parent->dr = nodnou;
        }
    }
}

思维方式:此函数获取我们要插入的节点的值作为k参数。 该函数只会插入树的根(根是全局变量)。

我认为我最大的问题是while循环遍历树以查找新节点的位置。

如果我使用while (flow!=NULL) ,它将不起作用,因为流指针获得了对不存在对象的赋值。 请帮助我了解我错了(作业)。

您的代码有几个重要的缺陷,其中最重要的一个就是对动态内存分配在C中的工作方式的误解。 永远不要遵循这样的模式:

Type *pointer = malloc(sizeof(Type));
pointer = <<something else>>

从字面上看,它会泄漏内存,并且在短短两行之内就没有任何帮助 这不是像Java或C#这样的基于对象引用的语言。 指针是保存内存地址的变量。 就像int可以容纳整数一样,指针也可以容纳address 就像下面的例子一样:

int n = 6;
n = 5;      //Hmm. Where did the 6 go? Oh yeah, We overwrote it with 5. 

您将失去使用指针执行相同操作的分配链接:

struct node *root = malloc(sizeof(*root));
root = nodnou; // memory allocated above is gone. forever leaked.

指针是变量 就像任何其他变量一样,它们保存值。 但是,在使用指针的情况下,其值为address 您可以在C语言中拥有几乎所有内容的指针,包括指向指针的指针。 保存指针变量地址的变量。 我之所以提出这些建议,是因为它们为您的插入要求提供了特别优雅的解决方案。

以下是二进制树插入的一般实现,该插入不支持树中的重复项(如果允许重复项,则代码会更短)。 此外,它使用提供的函数参数之外的零个局部变量来执行此操作,因此我挑战您剖析并确定其工作方式。 它甚至可以在最初为NULL的树根指针上工作,从而无需使用特殊的大小写if (root) {} else {}逻辑:

void insert_node(struct node **pp, int k) 
{
    while (*pp)
    {
        if (k < (*pp)->nr)        // move down left side?
            pp = &(*pp)->st;

        else if ((*pp)->nr < k)   // move down right side?
            pp = &(*pp)->dr;

        else return;              // found the key, no dupes. leave
    }

    // pp contains the address of the pointer we need to set.
    *pp = malloc(sizeof(**pp)); 
    (*pp)->st = (*pp)->dr = NULL;
    (*pp)->nr = k;
}

如果您的树应支持重复项,则需要对其插入的哪一侧保持一致,但会大大缩短上述算法:

void insert_node(struct node **pp, int k) 
{
    while (*pp)
        pp = (k < (*pp)->nr) ? &(*pp)->st : &(*pp)->dr;

    // pp contains the address of the pointer we need to set.
    *pp = malloc(sizeof(**pp)); 
    (*pp)->st = (*pp)->dr = NULL;
    (*pp)->nr = k;
}

无论哪种情况,都可以在调用方调用,如下所示:

struct node *root = NULL;

insert(&root, 5);
insert(&root, 10);
insert(&root, 7); 
...etc...

我认为您应该使用while(flow!= NULL)并在此之后将元素作为流插入。 现在的方式是在不应该停止的情况下停止运行,并在停止时进行奇怪的操作。 尝试使用笔和纸研究一些示例。

差不多了 赶上!

首先,您需要了解更好的内存分配。 实际上,您只需要在函数中进行第一个malloc()调用。 那就是您在每次insert_node()调用期间为要附加到树上的节点分配的内存。 您正在执行的所有剩余malloc都是不必要的。 似乎您从直觉上感觉需要为正在使用的其他指针分配内存,但是它们全部都是临时的,不需要任何分配,只需在尝试取消引用它们之前就分配给有效节点。 实际上,这些不必要的分配会在如下代码中创建所谓的内存泄漏(您请求的内存而无法释放):

root = (struct node*)malloc(sizeof(node));
root = nodnou;

第二个Assignmet( root = nodnou )覆盖前一个malloc()调用的结果,由于您没有将覆盖的指针值保存在其他任何地方,因此您将无法再释放该内存,它将被标记为用于您的应用程序的生命周期!

接下来,您可以简化遍历树寻找插入点的代码。 您似乎担心流变为NULL,但这无关紧要。 重要节点是父节点。 while循环结束后,它将指向需要链接插入的节点的实际节点。 这是您代码的修改版本。

void insert_node(int k) {
   struct node *nodnou, *flow, *parent;
   // this is the only memory allocation that should be done
   nodnou = (struct node*)malloc(sizeof(node));
   nodnou->st = NULL;
   nodnou->dr = NULL;
   nodnou->nr = k;

   parent = NULL;

   if( root == NULL ) {
      root = nodnou;
   } else {

      flow = root;
      // We will walk the tree in order until we reach the bottom of the 
      // tree (flow becomes null). What we are trying to do is to find out
      // the node that will become the parent of the new node we are inserting
      // It doesn't matter if flow becomes NULL, the important value after the
      // while loop ends is parent 
      while( flow != NULL ) {
         // update the current potential parent node
         parent = flow;
         if( k < flow->nr ) {
            // the number we are inserting is lower than the flow node, walk to the left
            flow = flow->st;
         } else {
            // the number we are inserting is greater or equal than the flow node, 
            // walk to the right
            flow = flow->dr;
         }
      }

      // We have reached the bottom, now compare number again with the node that
      // will become parent, to find out if we need to link this node to the left
      // or to the right of the parent node

      if( k < parent->nr ) {
         parent->st = nodnou;
      } else {
         parent->dr = nodnou;
      }
   }

}

而已。 尝试对其余的树操作进行编码,不要犹豫,请问是否感到困惑。 =)

暂无
暂无

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

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