簡體   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