【问题标题】:How to print all words in a Trie?如何打印 Trie 中的所有单词?
【发布时间】:2012-12-03 14:50:28
【问题描述】:

我正在尝试用 C++ 创建一个 Trie 实现。我不知道如何打印存储在Trie 中的所有单词。

这就是我实现TrieNode的方式。

struct TrieNode{
  bool isWord;
  int data; //Number of times Word Occured
  TrieNode *Child[ALPHABET_SIZE]; //defined as 26
};

我知道我可以将pointer 存储到父节点,深度优先搜索isWord==True 所在的所有节点并递归打印这些节点中的每个单词。

但我想知道有没有办法通过我的TrieNode 实现打印出Trie 中的每个单词。

感谢您的帮助。

【问题讨论】:

  • data 是什么?我了解isWordChild 数组(为什么不children?)给孩子们...但是data 代表什么?
  • 抱歉,澄清一下。它是包含单词在文本文档中出现的次数。

标签: c++ data-structures trie


【解决方案1】:

这是一个相当有效的 Konrad Rudolph 版本,没有假设 data 是一个字符。我还删除了在 Konrad 版本中分配的 O(n^2) 总内存,代价是使用 std::string&。这个想法是传递前缀并在每次递归时对其进行修改,将字符推到末尾然后弹出它,最终比疯狂复制它更有效。

void traverse(std::string& prefix, TrieNode const& node) {
  if (node.isWord)
    print(prefix);

  for (char index = 0; index < ALPHABET_SIZE; ++index) {
    char next = 'a'+index;
    TrieNode const* pChild = node.Child[index];
    if (pChild) {
      prefix.push_back(next);
      traverse(prefix, *pChild);
      prefix.pop_back();
    }
  }
}

【讨论】:

  • 注意:您可以使用std::string&amp; prefix 而不会丢失功能,print 的代码会更简单。
  • 是的,但是我必须查找正确的语法来伪造pop_backstd::string,我很着急。显然它已经被添加到 C++11 中了?得分!
  • 逻辑对我很有帮助。谢谢
【解决方案2】:

我认为这里不需要 isword。指向子节点的指针的存在将足以遍历 trie 中的可用单词。 要查找单词,请从根开始,并在任何递归步骤中查找单词中的当前字符。

struct trie {
  trie *children[ALPHABET_SIZE];
};


void traversal(trie *&t, string &str) {
    bool is_seen = false;
    for(int i = 0; i < ALPHABET_SIZE; i++) {
        if(t->children[i]) {
            if(!is_seen) {
               is_seen = true;
            }
            str.push_back(t[i]);
            traversal(t->children[i], str);
            str.pop_back();
        }
    }
    if(!is_seen) {
        cout << str << "\n";
    }

}

【讨论】:

  • 不是每个节点都是一个词。您必须标记它或使用 word_string 对象来存储单词;空词表示字典中没有该词。
【解决方案3】:

您不需要父节点,您的代码很容易通过递归适应遍历。伪代码:

void traverse(string prefix, TrieNode const& n) {
    prefix += static_cast<char>(n.data);

    if (n.isWord)
        print(prefix);

    for (auto const next : n.Child)
        if (next)
            traverse(prefix, *next);
}

这或多或少是有效的 C++。只需适当地定义print

编辑为了回应 Yakk 的评论和您的澄清,这是一个不假设 data 包含当前字符的版本(我的失误!):

void traverse(string const& prefix, TrieNode const& n) {
    if (n.isWord)
        print(prefix);

    for (std::size_t i = 0; i < ALPHABET_SIZE; ++i)
        if (n.child[i])
            traverse(prefix + ('a' + i), *n.child[i]);
}

我将把更有效的实现留给 Yakk 的回答。

【讨论】:

  • 你为什么认为data 是一个字符?
  • @MatthieuM。因为OP说这个结构代表一个trie。还应该是什么?您需要将节点的字符数据存储在某处。 (但是是的,data 的类型应该是 char,而不是 int。)
  • @KonradRudolph,看我的回答——子指针的索引告诉你下一个字符是什么,所以你不需要将它实际存储在节点中。
【解决方案4】:
void traversePrint(TrieNode* sr,char* out,int index)
{
    if(sr!=NULL)
    {
        for(int i=0;i<SIZE;i++)
        {
            if(sr->child[i]!=NULL)
            {
                out[index] = 'a'+i;
                traversePrint(sr->child[i],out,index+1);
            }
        }
        if(sr->isEnd)
        {
            out[index]='\0';
            cout << out << endl;
        }
    }
}

// 调用

char out[MAX_WORD_SIZE];
traversePrint(root,out,0);

【讨论】:

    猜你喜欢
    • 2022-11-25
    • 2017-07-18
    • 2016-05-17
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2013-04-07
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多