【问题标题】:How to populate a trie in C?如何在 C 中填充 trie?
【发布时间】:2014-11-01 23:20:19
【问题描述】:

我正在尝试编写一个程序,该程序接受单词并创建一个 trie,其中 trie 的每个节点都是一个包含单个字符的结构。

我有一个函数可以将 char* 解析为单词(假设 char* 只包含小写字母)。由于每个单词都取自 char*,因此它被传递给函数addWordOccurrence(const char* word, const int wordLength, struct tNode root)addWordOccurrence() 应该检查单词的第一个字母是否在 root.branches[i] 中,因为 i 在循环中递增,检查 root.branches 的每个可能索引(对于字母表的所有小写字母,它都是 0-25) .如果第一个字母不在root.branches 中,则创建一个包含新字母的新结构tNode。然后继续到单词的第二个字母,将其与新创建的结构 tNode 的分支进行比较,依此类推...

我们尝试的第一个单词是“doctor”,我的 trie 将第一个字母“d”添加到root.branches[0],然后将“o”添加到root.branches[0].branches[0],这是正确的。但随后它将医生中的“d”添加到其分支的接下来的 17 个索引中(所以root.branches[0].branches[1] through [18]),这不应该是这种情况。请帮忙!

struct tNode{
  char c;
  int occurrences;
  struct tNode *branches;
};

int addWordOccurrence(const char* word, const int wordLength, struct tNode root){
//declare fields
int counter, i,k,firstNull;
counter = 0;
while(1){
  if(counter >= wordLength){
    break;
  }
  //traverse through the word letter by letter
  for(i=0; i<wordLength; i++){
    //compare each letter to the branches of root until the letter is found or first null space
    for(k=0; k<26; k++){
    //if the letter is a branch already set root to the struct of that letter in branches
       if(root.branches[k].c == word[i]){
          root = root.branches[k];
          break;
       }
    }
    //the current letter of the word is not in branches
    //go through branches to find position to add the new tNode
    for(firstNull=0; firstNull<26; firstNull++){
       //set firstNull equal to the index of the first null value in branches
       if(root.branches[firstNull].c  <  'a' || root.branches[firstNull].c > 'z' ){
          break;
       }
    }
    //add a new node to branches
    root.branches[firstNull].c = word[i];
    root.branches[firstNull].occurrences = 0;
    root.branches[firstNull].branches = malloc(sizeof(struct tNode) * 26);
    if(counter != wordLength){
       root = root.branches[firstNull];
    }
    counter++;
    if(counter == wordLength-2){
       root.occurrences++;
    }
 }
}
 return 0;
}

【问题讨论】:

  • 你认为第一个break在做什么?我敢打赌,它不会那样做。
  • 最初 while 循环末尾的 root.occurrences++ 位于 while 之外,因此在读取单词的最后一个字母后,它会增加 'r'(如果单词是 '医生') tNode.occurrences 添加了最后一个字母,但是当我调试它时 tNode.occurrence 的值应该是 1 时是 0,所以中断是退出 while 循环......我已经改变了很多次,我看着它快疯了,对此感到抱歉。

标签: c struct trie


【解决方案1】:

你的实现有一堆问题:

  1. 这是一个奇怪的 trie 设计,具有随机排列的字母表。必须在每个级别上对您想要的字母进行线性搜索,这违背了首先进行尝试的目的。
  2. 当您执行root = root.branches[k]; 时,您正在创建变量的副本。现在,由于通过指针访问事物,在这种情况下它可能会为您工作,但它实际上只是在自找麻烦。
  3. 当您在循环中分配一个节点时,您不会对其进行初始化,这意味着它充满了垃圾/未知数据并导致问题。
  4. 您的实现过于复杂,就像您的外部 while (1) 循环一样。

对于一个非常简单的尝试,我会这样做:

struct tNode {
  bool isWord;
  struct tNode *branches[26];
};

void addWordOccurrence (const char* word, const int wordLength, struct tNode* pRoot) {
   int i;
   int nodeIndex;
   tNode* pCurrentNode = pRoot;

   for (i = 0; i < wordLength; ++i)
   {
       nodeIndex = tolower(word[i]) - 'a'; 

       if (nodeIndex >= 0 && nodeIndex <= 25)
       {
            if (pCurrentNode->branches[nodeIndex] == NULL)
            {
                pCurrentNode->branches[nodeIndex] = calloc(1, sizeof(tNode));
            }

            pCurrentNode = pCurrentNode->branches[nodeIndex];
       }
   }

   pCurrentNode->isWord = true;
}

您可以使用struct tNode *branches;,但它实际上只是添加了另一个您并不真正需要的分配步骤。您使用字符的 ASCII 值将 branches[0] 分配给 'a' 并将 branches[25] 分配给 'z'...无需搜索会真正破坏 trie 性能的“空闲”位置。最后,您需要像isWord 这样的终止符,以便知道“医生”是一个词,而“医生”不是。

【讨论】:

    猜你喜欢
    • 2015-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 1970-01-01
    • 2011-09-18
    相关资源
    最近更新 更多