【经典数据结构】Trie

  在计算机科学中,trie,又称前缀树字典树,是一种有种树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。

  本质上,Trie是存储多个字符串的树。

  Trie树的思想是利用字符串的公共前缀降低时空开销

  Trie树的典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

  Trie树最大的优点是最大限度减少无谓的字符串比较。

  Trie树的缺点是如果存在大量字符串且这些字符串没有公共前缀,则相应的Trie树将非常消耗内存。

以下几个博客的讲解很详细:

1. http://blog.csdn.net/hackbuteer1/article/details/7964147

2. http://blog.csdn.net/v_july_v/article/details/6897097

3. http://blog.csdn.net/luxiaoxun/article/details/7937589

4. 《王道程序员求职宝典》 P276

5. geeksforgeeks trie树讲解。

 

使用范围:

  1. 词频统计 2.前缀匹配 3.全词匹配

 

 

 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 
 5 class Trie{
 6 public:
 7     Trie();
 8     ~Trie();
 9     void Insert(const string &s);
10     bool Search(const string &s) const;
11 private:
12     struct TrieNode{
13             int count; //单词出现的次数
14             TrieNode *next[26]; 16 
17             TrieNode(): count(0), exist(false) {
18                 memset(next, NULL, sizeof(next));
19             }
20     };
21 
22     TrieNode *root_;
23 
24     void MakeEmpty(TrieNode *root);
25 };
26 
27 Trie::Trie() {
28     root_ = new TrieNode();
29 }
30 
31 Trie::~Trie() {
32     MakeEmpty(root_);
33 }
34 
35 void Trie::Insert(const string &s) {
36     int len = s.size();
37     TrieNode *position = root_;
38     for (int i =0; i < len; ++i) { //不存在建立一个节点
39         if (position->next[s[i] - 'a'] == NULL) {
40             position->next[s[i] -'a'] = new TrieNode();
41         }
42         position = position->next[s[i] - 'a']; //每插入一步,相当于一个新串经过,指针要向下移动
43     }
44 
45     position->count++;47 }
48 
49 bool Trie::Search(const string &s) const {
50     if (root_ == NULL) return false;
51 
52     int len = s.size();
53     TrieNode *location = root_;
54     for (int i = 0; i < len && location != NULL; ++i) {
55         if (location->next[s[i] - 'a'] == NULL) {
56             return false;
57         } 
58         location = location->next[s[i] - 'a'];
59     }
60 
61     return p != NULL && location->count != 0;
} 63 64 void Trie::MakeEmpty(TrieNode *root) { 65 for (int i = 0; i < 26; ++i) { 66 if (root->next[i] != NULL) 67 MakeEmpty(root->next[i]); 68 } 69 70 delete root; 71 root = NULL; 72 }

 

建树的时间复杂度为O(len * n),n表示单词数量,len表示单词平均长度。

Trie的空间复杂度为O(sizeof(next) * len * n).冗余度较高,更高效的存储方法是compressed trie和ternary search tree,

Trie树的应用:

    • 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
    • 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
    •  一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
    • 寻找热门查询:
    • 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。

posted @ 2015-03-13 15:52  vincently  阅读(367)  评论(0编辑  收藏  举报