实现效率

时间:2012-01-04 01:57:58

标签: c performance data-structures tree trie

哪个更有效率。像这样的Trie结构:

struct TrieNode              
{
char letter;              
bool isWord;                
     TrieNode* subNodes[26]; 
};

或像这样的Trie结构:

struct TrieNode
{ 
    char letter;
    bool isword;
    map<int, TrieNode*> subNodes;
};

或者只是一个更好的实施...... 还有,有人能给我一个解释吗?

2 个答案:

答案 0 :(得分:1)

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

在任一代码中都不需要char letter元素。 它是多余的,因为你查找单词的方式是获取键的一个字母,并将其用作subNode数组的索引或地图中的键,以便选择一个subNode。 无论哪种方式,您都不需要查看letter

你知道单词是否在trie中的方式是你是否遇到了一个空子节点,或者你是否在没有点击isWord子节点的情况下耗尽你的密钥。

顺便说一句,如果你的trie不包含太多的单词,并且如果它不经常变化,你将通过将其转换为ad-hoc代码来节省大约一个数量级的速度。


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

// 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。在每个节点,编译器将确定它是否需要跳转表,因此您不必担心。

答案 1 :(得分:1)

我曾经采用第一种方法(即每个节点都有一个子字母用于字母表的每个可能的字母),但是意识到这是非常低效的(空间明智的)并假设你总是有一个恒定的算法。

如果您改为使用链表替换数组(然后对其进行操作),您可以使用二叉树实现(但是结构仍然比传统的二进制树更有效,因为您不是在每个节点使用字符串比较,并且因为你的密钥空间重叠(找到“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通常会非常稀疏,所以我们永远不会接近实际的最坏情况。