【发布时间】:2010-11-05 09:49:38
【问题描述】:
在 C/C++ 中是否有任何速度和缓存高效的 trie 实现? 我知道 trie 是什么,但我不想重新发明轮子,自己实现它。
【问题讨论】:
标签: c++ c data-structures trie
在 C/C++ 中是否有任何速度和缓存高效的 trie 实现? 我知道 trie 是什么,但我不想重新发明轮子,自己实现它。
【问题讨论】:
标签: c++ c data-structures trie
如果您正在寻找 ANSI C 实现,您可以从 FreeBSD 中“窃取”它。您要查找的文件名为radix.c。用于管理内核中的路由数据。
【讨论】:
我意识到问题是关于现成的实现,但仅供参考......
在你跳上朱迪之前,你应该阅读“A Performance Comparison of Judy to Hash Tables”。然后在谷歌上搜索标题可能会给您带来一生的讨论和反驳。
我所知道的一个明确的缓存意识 trie 是HAT-trie。
正确实施 HAT-trie 非常酷。但是,对于前缀搜索,您需要对哈希桶进行排序,这与前缀结构的想法有些冲突。
一个更简单的树是burst-trie,它本质上为您提供了某种标准树(如 BST)和树之间的插值。我喜欢它的概念,而且它更容易实现。
【讨论】:
libTrie 我很幸运。它可能没有经过专门的缓存优化,但对于我的应用程序来说性能一直不错。
【讨论】:
GCC 附带了一些数据结构作为其“基于策略的数据结构”的一部分。这包括一些 trie 实现。
http://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/trie_based_containers.html
【讨论】:
#include 的样品吗?谢谢你的回答。
参考资料,
C 实现)【讨论】:
Judy arrays:用于位、整数和字符串的非常快速且内存高效的有序稀疏动态数组。 Judy 数组比任何二叉搜索树(包括 avl 和红黑树)都更快且内存效率更高。
【讨论】:
您也可以通过 http://tommyds.sourceforge.net/ 试用 TommyDS
查看网站上的基准页面,与 nedtries 和 judy 进行速度比较。
【讨论】:
缓存优化是您可能必须要做的事情,因为您必须将数据放入单个缓存行中,该缓存行通常约为 64 字节(如果您开始组合数据,这可能会起作用,例如作为指针)。但这很棘手:-)
【讨论】:
Burst Trie's 似乎更节省空间。我不确定您可以从任何索引中获得多少缓存效率,因为 CPU 缓存非常小。但是,这种 trie 足够紧凑,可以将大型数据集保存在 RAM 中(常规 Trie 不会)。
我编写了一个 Burst trie 的 Scala 实现,它还结合了我在 GWT 的 type-ahead 实现中发现的一些节省空间的技术。
【讨论】:
与我的数据集提到的几个 TRIE 实现相比,我使用 marisa-trie 获得了非常好的结果(性能和大小之间的非常好的平衡)。
【讨论】:
分享我对 Trie 的“快速”实现,仅在基本场景中进行了测试:
#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;
}
}
};
【讨论】: