簡體   English   中英

插入 AVL 樹只替換根節點

[英]Insertion into AVL tree only replaces root node

我目前正在執行一項任務,其中必須打印一本書 (.txt) 中最常用的 N 個單詞。 我目前面臨的問題是,當我將一個節點添加到我的一棵樹時,它只是替換了根節點,因此,樹仍然是一個節點。

將文件“stopwords.txt”中的單詞添加到名為 stopwords 的樹中的代碼片段:

Dict stopwords = newDict();

if (!readFile("stopwords.txt"))
   {
      fprintf(stderr, "Can't open stopwords\n");
      exit(EXIT_FAILURE);
   }

   FILE *fp = fopen("stopwords.txt", "r");

   while (fgets(buf, MAXLINE, fp) != NULL)
   {
      token = strtok(buf, "\n");
      DictInsert(stopwords, buf); //the root is replaced here
   }
   fclose(fp);

數據結構定義如下:

typedef struct _DictNode *Link;

typedef struct _DictNode
{
   WFreq data;
   Link left;
   Link right;
   int height;
} DictNode;

typedef struct _DictRep *Dict;

struct _DictRep
{
   Link root;
};

typedef struct _WFreq {
   char  *word;  // word buffer (dynamically allocated)
   int    freq;  // count of number of occurences
} WFreq;

插入和重新平衡樹的代碼:

// create new empty Dictionary
Dict newDict(void)
{
   Dict d = malloc(sizeof(*d));
   if (d == NULL)
   {
      fprintf(stderr, "Insufficient memory!\n");
      exit(EXIT_FAILURE);
   }
   d->root = NULL;
   return d;
}

// insert new word into Dictionary
// return pointer to the (word,freq) pair for that word
WFreq *DictInsert(Dict d, char *w)
{
   d->root = doInsert(d->root, w); //the root is replaced here before doInsert runs
   return DictFind(d, w);
}

static int depth(Link n)
{
   if (n == NULL)
      return 0;
   int ldepth = depth(n->left);
   int rdepth = depth(n->right);
   return 1 + ((ldepth > rdepth) ? ldepth : rdepth);
}

static Link doInsert(Link n, char *w)
{
   if (n == NULL)
   {
      return newNode(w);
   }

   // insert recursively
   int cmp = strcmp(w, n->data.word);
   if (cmp < 0)
   {
      n->left = doInsert(n->left, w);
   }
   else if (cmp > 0)
   {
      n->right = doInsert(n->right, w);
   }
   else
   { // (cmp == 0)
      // if time is already in the tree,
      // we can return straight away
      return n;
   }

   // insertion done
   // correct the height of the current subtree
   n->height = 1 + max(height(n->left), height(n->right));

   // rebalance the tree
   int dL = depth(n->left);
   int dR = depth(n->right);

   if ((dL - dR) > 1)
   {
      dL = depth(n->left->left);
      dR = depth(n->left->right);
      if ((dL - dR) > 0)
      {
         n = rotateRight(n);
      }
      else
      {
         n->left = rotateLeft(n->left);
         n = rotateRight(n);
      }
   }
   else if ((dR - dL) > 1)
   {
      dL = depth(n->right->left);
      dR = depth(n->right->right);
      if ((dR - dL) > 0)
      {
         n = rotateLeft(n);
      }
      else
      {
         n->right = rotateRight(n->right);
         n = rotateLeft(n);
      }
   }

   return n;
}

static Link newNode(char *w)
{
   Link n = malloc(sizeof(*n));
   if (n == NULL)
   {
      fprintf(stderr, "Insufficient memory!\n");
      exit(EXIT_FAILURE);
   }

   n->data.word = w;
   n->data.freq = 1;
   n->height = 1;
   n->left = NULL;
   n->right = NULL;
   return n;
}

// Rotates  the  given  subtree left and returns the root of the updated
// subtree.
static Link rotateLeft(Link n)
{
   if (n == NULL)
      return n;
   if (n->right == NULL)
      return n;
   Link rightNode = n->right;
   n->right = rightNode->left;
   rightNode->left = n;

   n->height = max(height(n->left), height(n->right)) + 1;
   rightNode->height = max(height(rightNode->right), n->height) + 1;

   return rightNode;
}

// Rotates the given subtree right and returns the root of  the  updated
// subtree.
static Link rotateRight(Link n)
{
   if (n == NULL)
      return n;
   if (n->left == NULL)
      return n;
   Link leftNode = n->left;
   n->left = leftNode->right;
   leftNode->right = n;

   n->height = max(height(n->left), height(n->right)) + 1;
   leftNode->height = max(height(leftNode->right), n->height) + 1;

   return leftNode;
}

我相信大部分代碼都是有效的,只是插入失敗了。 當我嘗試使用 gdb 進行調試時,我發現根節點(d->root)在遞歸插入 function(doInsert)運行之前被替換,導致程序總是返回節點 n,結果,已經存在於樹中。 例如,如果文本文件包含以下內容:
一個
b
c
然后程序會首先插入"a"作為stopwords->root ,然后"b"將替換"a"並成為新的stopwords->root ,最后"c"將替換"b"作為stopwords->root ,結果在具有一個節點的樹中, "c"

您的代碼中有許多不一致之處。

這里有一個錯誤:

d->root = doInsert(d->root, w);

每次插入新節點時,您都會無條件地重新分配根。

您應該從 function doInsert返回新節點,並且僅當新節點已成為新根時才重新分配根。

但是你犯的另一個錯誤是你從doInsert返回了一個局部變量n ,它不是新分配的,而是初始化為指向前一個根的。

doInsert內部,您需要分配一個新節點NEW並使用變量x從根向下走,直到找到插入新分配節點NEW的位置。 如果x在 root 處停止,則重新初始化d->root = NEW

您的 function newNode僅存儲傳遞的字符串指針,因此當您修改原始字符串時,指向的內容會發生變化。

為防止這種情況,您應該在節點插入時復制輸入字符串。

要存檔,

    n->data.word = w;

應該

    n->data.word = malloc(strlen(w) + 1);
    if (n->data.word == NULL)
    {
        fprintf(stderr, "Insufficient memory!\n");
        exit(EXIT_FAILURE);
    }
    strcpy(n->data.word, w);

添加#include <string.h>以使用strlen()strcpy()如果不是。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM