简体   繁体   English

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

[英]Binary Search Tree node insertion not working properly

I have a homework assignment to implement a binary search tree (create, delete, search). 我有一个作业分配来实现一个二进制搜索树(创建,删除,搜索)。 I used the example provided by the teacher but I can't make it work. 我使用了老师提供的示例,但无法使其正常工作。

Here's my code so far: 到目前为止,这是我的代码:

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

The way of thinking: This function gets the value of the node we want to insert as the k parameter. 思维方式:此函数获取我们要插入的节点的值作为k参数。 The function will only insert the root of the tree (root is global variable). 该函数只会插入树的根(根是全局变量)。

I think my biggest problem is the while loop that sweeps through the tree to find the spot for the new node. 我认为我最大的问题是while循环遍历树以查找新节点的位置。

If I use while (flow!=NULL) it won't work because the flow pointer gets an assignment to something that does not exist. 如果我使用while (flow!=NULL) ,它将不起作用,因为流指针获得了对不存在对象的赋值。 Please help me understand where I am wrong (homework). 请帮助我了解我错了(作业)。

Your code has several important flaws, not the least of which is a misunderstanding of how dynamic memory allocation works in C. Never follow a pattern like this: 您的代码有几个重要的缺陷,其中最重要的一个就是对动态内存分配在C中的工作方式的误解。 永远不要遵循这样的模式:

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

It literally leaks memory and gains you nothing in two short lines. 从字面上看,它会泄漏内存,并且在短短两行之内就没有任何帮助 This isn't an object-reference based language like Java or C#. 这不是像Java或C#这样的基于对象引用的语言。 Pointers are variables that hold memory addresses. 指针是保存内存地址的变量。 Just like an int can hold an integer, a pointer holds an address . 就像int可以容纳整数一样,指针也可以容纳address And just like the following example: 就像下面的例子一样:

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

You will lose your allocation link doing the same thing with pointers: 您将失去使用指针执行相同操作的分配链接:

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

Pointers are variables . 指针是变量 Just like any other variable, they hold values. 就像任何其他变量一样,它们保存值。 In the case of a pointer, however, its value is an address . 但是,在使用指针的情况下,其值为address You can have pointers to almost anything in C, including pointers to pointers; 您可以在C语言中拥有几乎所有内容的指针,包括指向指针的指针。 variables that hold the address of pointer variables. 保存指针变量地址的变量。 And I bring them up because they proffer a particularly elegant solution to your insertion requirements. 我之所以提出这些建议,是因为它们为您的插入要求提供了特别优雅的解决方案。

The following is a general implementation for a binary tree insertion that supports no duplicates in the tree (the code gets even shorter if you allow duplicates). 以下是二进制树插入的一般实现,该插入不支持树中的重复项(如果允许重复项,则代码会更短)。 Furthermore, it does this using exactly zero local variables beyond the provided function parameters, and I challenge you to dissect this and determine how it works. 此外,它使用提供的函数参数之外的零个局部变量来执行此操作,因此我挑战您剖析并确定其工作方式。 It even works on an initially NULL tree root pointer, eliminating the need to special casing if (root) {} else {} logic: 它甚至可以在最初为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;
}

If your tree should support duplicates you need to be consistent about which side they are inserted on, but it shortens the above algorithm considerably: 如果您的树应支持重复项,则需要对其插入的哪一侧保持一致,但会大大缩短上述算法:

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

In either case, invoked on the caller side like this: 无论哪种情况,都可以在调用方调用,如下所示:

struct node *root = NULL;

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

I think you should use while(flow != NULL) and insert your element as flow after that. 我认为您应该使用while(flow!= NULL)并在此之后将元素作为流插入。 The way it is right now it will stop in cases when it shouldn't and do weird things whenever it stops. 现在的方式是在不应该停止的情况下停止运行,并在停止时进行奇怪的操作。 Try working through some examples with pen and paper. 尝试使用笔和纸研究一些示例。

You almost got it. 差不多了 Keep Up! 赶上!

First you need to understand a bit better memory allocation. 首先,您需要了解更好的内存分配。 In reality, you only need the very first malloc() call in your function. 实际上,您只需要在函数中进行第一个malloc()调用。 That is the memory you allocate for the node you are appending to the tree during each insert_node() call. 那就是您在每次insert_node()调用期间为要附加到树上的节点分配的内存。 All remainingr malloc s you are performing are unnecesary. 您正在执行的所有剩余malloc都是不必要的。 It seems that you intuitively feel you need to allocate memory for the other pointers you are using, but all of them are temporary and don't require any allocation, just assignment to a valid node before attempting to de-reference them. 似乎您从直觉上感觉需要为正在使用的其他指针分配内存,但是它们全部都是临时的,不需要任何分配,只需在尝试取消引用它们之前就分配给有效节点。 In fact, those unnecesary allocations will create what is known as a memory leak (memory you request and fail to release) in code like this: 实际上,这些不必要的分配会在如下代码中创建所谓的内存泄漏(您请求的内存而无法释放):

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

The second assignmet ( root = nodnou ) overwrites the result of the previous malloc() call and since you didn't save the overwritten pointer value in any other place, you will no longer be able to release that memory, it will be marked as used for the lifetime of your application! 第二个Assignmet( root = nodnou )覆盖前一个malloc()调用的结果,由于您没有将覆盖的指针值保存在其他任何地方,因此您将无法再释放该内存,它将被标记为用于您的应用程序的生命周期!

Next, you can simplify the code that is walking the tree looking for the insertion point. 接下来,您可以简化遍历树寻找插入点的代码。 You seem to worry that flow becomes NULL, but it doesn't matter. 您似乎担心流变为NULL,但这无关紧要。 The important node is parent. 重要节点是父节点。 After the while loop ends, it will be pointing to the actual node where the inserted node needs to be linked. while循环结束后,它将指向需要链接插入的节点的实际节点。 Here is a modified version of your code. 这是您代码的修改版本。

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

}

That's it. 而已。 Try to code the rest of the tree operations and don't hesitate to ask if you become confused. 尝试对其余的树操作进行编码,不要犹豫,请问是否感到困惑。 =) =)

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

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