C Trie实施效率

C Trie实施效率,c,performance,data-structures,tree,trie,C,Performance,Data Structures,Tree,Trie,这是一个更有效的方法。这样的Trie结构: struct TrieNode { char letter; bool isWord; TrieNode* subNodes[26]; }; struct TrieNode { char letter; bool isword; map<int, TrieNode*> subNodes; }; // XYZ is

这是一个更有效的方法。这样的Trie结构:

struct TrieNode              
{
char letter;              
bool isWord;                
     TrieNode* subNodes[26]; 
};
struct TrieNode
{ 
    char letter;
    bool isword;
    map<int, TrieNode*> subNodes;
};
// XYZ is the prefix string that corresponds to a node in the trie
bool XYZFunc(char* key){
    switch (*key){
    case '\0': return true /* if XYZ is a valid word, else false */; break;
    case 'a': return XYZaFunc(key+1); break;
    case 'b': return XYZbFunc(key+1); break;
    // etc. etc.
    }
}
或者像这样的Trie结构:

struct TrieNode              
{
char letter;              
bool isWord;                
     TrieNode* subNodes[26]; 
};
struct TrieNode
{ 
    char letter;
    bool isword;
    map<int, TrieNode*> subNodes;
};
// XYZ is the prefix string that corresponds to a node in the trie
bool XYZFunc(char* key){
    switch (*key){
    case '\0': return true /* if XYZ is a valid word, else false */; break;
    case 'a': return XYZaFunc(key+1); break;
    case 'b': return XYZbFunc(key+1); break;
    // etc. etc.
    }
}
结构三节点 { 字符字母; 布尔语; 映射子节点; }; 还是有一个更好的实施方案。。。
另外,有人能给我一个解释吗?

为了简单和快速,我会使用第一个,但可以想象第二个可以节省空间

这两种代码中都不需要
char-letter
元素。 这是多余的,因为查找单词的方式是获取关键字的字母,并将其用作子节点数组的索引,或用作映射的关键字,以便选择子节点。 无论哪种方式,您都不需要查看
字母

您知道单词是否不在trie中的方法是,如果您点击一个空子节点,或者如果您在没有点击
isWord
子节点的情况下耗尽了键

顺便说一句,如果您的trie没有包含太多的单词,并且没有经常更改,那么通过将其转换为特定代码,您将始终节省大约一个数量级的速度


我所说的特殊代码的意思是,trie是一种有限状态机,而有限状态机是一种程序。因此,您编写一个程序来读取已排序的字典,但它不是构建trie数据结构,而是用您最喜欢的语言编写一个程序,如下所示:

struct TrieNode              
{
char letter;              
bool isWord;                
     TrieNode* subNodes[26]; 
};
struct TrieNode
{ 
    char letter;
    bool isword;
    map<int, TrieNode*> subNodes;
};
// XYZ is the prefix string that corresponds to a node in the trie
bool XYZFunc(char* key){
    switch (*key){
    case '\0': return true /* if XYZ is a valid word, else false */; break;
    case 'a': return XYZaFunc(key+1); break;
    case 'b': return XYZbFunc(key+1); break;
    // etc. etc.
    }
}

这可能有很多函数,但在合理的范围内,编译器应该能够处理它。然后,要查找一个单词,只需调用顶级函数,它将返回true或false。在每个节点上,编译器将确定它是否需要跳转表,因此您不必担心这一点。

我曾经采用第一种方法(即每个节点都有一个子节点,对应于字母表中的每个可能字母),但是意识到这是非常低效的(从空间角度看),并假设您总是有一个常量算法

如果改为使用链表替换数组(然后对其进行一些操作),则可以使用二叉树实现(但是,该结构在查找时仍然比传统的二叉树更高效,因为您没有在每个节点上使用字符串比较,并且由于键空间重叠(查找“the”和查找“then”)从相同的比较开始)

我认为:

struct TrieNode
{
  char key;
  char *val; /* This is null unless we are an "end node" - you could use the Bool as you do, but I've found this a bit simpler */
  struct TrieNode *siblings; /* traversing this is checking different characters at this position in the string */
  struct TrieNode *children; /* Travesring this list is looking at subsequent positions in the list */
};


尽管在最坏的情况下,这种方法的效率开始下降,但字母表的大小决定了要检查的兄弟姐妹的最大数量,并决定了自然语言的排序(与基因组相反)trie通常非常稀疏,因此我们永远不会接近实际的最坏情况。

这是家庭作业吗?第二个代码就是不正确的。就这一点而言,第一个代码也是不正确的。这不是家庭作业。我只是试图扩展我对数据结构的知识。我只是为了简单起见提供了数据类型。你能澄清一下是什么吗我缺少?或者如何更好地实现Trie?谢谢。事实上,这两种方法都不有效。您应该实现双数组Trie。这样,为什么要创建单独的函数,只需将所有内容放在一个开关案例中。一些编译器实际上对开关案例使用Trie。您能否提供一个示例,说明为什么/何时使用兄弟姐妹函数会被利用吗?