实施
在C / C ++中有没有速度和高速caching效率的trie的实现? 我知道什么是特里,但我不想重新发明,自己实施。
如果你正在寻找一个ANSI C的实现,你可以从FreeBSD上“偷”它。 您正在查找的文件被称为radix.c 。 它用于pipe理内核中的路由数据。
我意识到这个问题是关于准备实现,但供参考…
在跳过Judy之前,你应该先阅读“ Judy到Hash表的性能比较 ”。 那么使用Googlesearch标题可能会给你一个一生的讨论和反驳阅读。
我知道的一个明确的caching意识的线索是HAT-trie 。
当正确实施时,HAT-trie非常酷。 但是,对于前缀search,您需要对散列桶进行sorting,这与前缀结构的想法有些冲突。
一个比较简单的trie是一个burst-trie ,它基本上给了你一个标准的树(比如BST)和一个trie之间的插值。 我从概念上喜欢它,实现起来要容易得多。
我已经和libTrie好运了。 它可能不是专门caching优化,但性能一直不错,我的应用程序。
作为“基于策略的数据结构”的一部分,GCC提供了一些数据结构。 这包括几个实现。
http://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/trie_based_containers.html
参考文献,
- 一个双数组Trie实现文章(包含一个
C
实现) - TRASH – 一个dynamicLC-trie和散列数据结构 – (一份2006年的PDF参考文献,描述了在Linux内核中用于在IP路由表中实现地址查找的dynamicLC-trie
Judy数组 :非常快速和高效的内存有序稀疏dynamic数组,用于位,整数和string。 Judy数组比任何二进制search树(包括avl&red-black-trees)更快,更高的内存效率。
你也可以在http://tommyds.sourceforge.net/上试试TommyDS
查看网站上的基准页面,以便与nedtries和judy进行速度比较。
Cedar , HAT-Trie和JudyArray相当棒,你可以在这里find基准。
caching优化是你可能必须要做的事情,因为你必须将数据放入一个通常类似于64字节的caching行(如果你开始组合数据,比如指针,这可能会起作用) 。 但是它很棘手:-)
Burst Trie似乎有更多的空间效率。 由于CPUcaching非常小,我不确定你能从任何索引中获得多lesscaching效率。 然而,这种types的Trie足够紧凑,可以将大量数据集保存在RAM中(常规Trie不会)。
我写了一个突发线程的Scala实现,这个实现还包含了我在GWT的types提前实现中find的一些节省空间的技术。
与我的数据集中提到的几个TRIE实现相比,我有非常好的结果(在性能和大小之间的非常好的平衡)与marisa-trie。
与Trie分享我的“快速”实施,仅在基本情况下进行testing:
#define ENG_LET 26 class Trie { class TrieNode { public: TrieNode* sons[ENG_LET]; int strsInBranch; bool isEndOfStr; void print() { cout << "----------------" << endl; cout << "sons.."; for(int i=0; i<ENG_LET; ++i) { if(sons[i] != NULL) cout << " " << (char)('a'+i); } cout << endl; cout << "strsInBranch = " << strsInBranch << endl; cout << "isEndOfStr = " << isEndOfStr << endl; for(int i=0; i<ENG_LET; ++i) { if(sons[i] != NULL) sons[i]->print(); } } TrieNode(bool _isEndOfStr = false):isEndOfStr(_isEndOfStr), strsInBranch(0) { for(int i=0; i<ENG_LET; ++i) { sons[i] = NULL; } } ~TrieNode() { for(int i=0; i<ENG_LET; ++i) { delete sons[i]; sons[i] = NULL; } } }; TrieNode* head; public: Trie() { head = new TrieNode();} ~Trie() { delete head; } void print() { cout << "Preorder Print : " << endl; head->print(); } bool isExists(const string s) { TrieNode* curr = head; for(int i=0; i<s.size(); ++i) { int letIdx = s[i]-'a'; if(curr->sons[letIdx] == NULL) { return false; } curr = curr->sons[letIdx]; } return curr->isEndOfStr; } void addString(const string& s) { if(isExists(s)) return; TrieNode* curr = head; for(int i=0; i<s.size(); ++i) { int letIdx = s[i]-'a'; if(curr->sons[letIdx] == NULL) { curr->sons[letIdx] = new TrieNode(); } ++curr->strsInBranch; curr = curr->sons[letIdx]; } ++curr->strsInBranch; curr->isEndOfStr = true; } void removeString(const string& s) { if(!isExists(s)) return; TrieNode* curr = head; for(int i=0; i<s.size(); ++i) { int letIdx = s[i]-'a'; if(curr->sons[letIdx] == NULL) { assert(false); return; //string not exists, will not reach here } if(curr->strsInBranch==1) { //just 1 str that we want remove, remove the whole branch delete curr; return; } //more than 1 son --curr->strsInBranch; curr = curr->sons[letIdx]; } curr->isEndOfStr = false; } void clear() { for(int i=0; i<ENG_LET; ++i) { delete head->sons[i]; head->sons[i] = NULL; } } };