Trie树

Trie树

       Trie树也称字典树,因为其效率很高,所以在在字符串查找、前缀匹配等中应用很广泛,其高效率是以空间为代价的。

一.Trie树的原理

    利用串构建一个字典树,这个字典树保存了串的公共前缀信息,因此可以降低查询操作的复杂度。

    下面以英文单词构建的字典树为例,这棵Trie树中每个结点包括26个孩子结点,因为总共有26个英文字母(假设单词都是小写字母组成)。

    则可声明包含Trie树的结点信息的结构体:

[cpp]  view plain copy
  1. typedef struct TrieNode  
  2.     {  
  3.         bool isStr;                       //标记该节点是否构成单词  
  4.         struct TrieNode *next[Max];  
  5.         TrieNode():isStr(false)  
  6.         {  
  7.             memset(next, NULL, sizeof(next));    //将next内存全部置为NULL  
  8.         }  
  9.     };  

特别注意:在节点构造结构体中,对每个节点的包含信息进行初始化,是很重要的,为后续public函数中额判断做好基础。 

其中next是一个指针数组,存放着指向各个孩子结点的指针。isStr 为标记一个串结束存在的标志。

    如给出字符串"abc","ab","bd","dda",根据该字符串序列构建一棵Trie树。则构建的树如下:


Trie树的根结点不包含任何信息,第一个字符串为"abc",第一个字母为'a',因此根结点中数组next下标为'a'-97的值不为NULL,其他同理,构建的Trie树如图所示,红色结点表示在该处可以构成一个单词。很显然,如果要查找单词"abc"是否存在,查找长度则为O(len),len为要查找的字符串的长度。而若采用一般的逐个匹配查找,则查找长度为O(len*n),n为字符串的个数。显然基于Trie树的查找效率要高很多。

但是却是以空间为代价的,比如图中每个结点所占的空间都为(26*4+1)Byte=105Byte,那么这棵Trie树所占的空间则为105*8Byte=840Byte,而普通的逐个查找所占空间只需(3+2+2+3)Byte=10Byte。

二.Trie树的操作

    在Trie树中主要有3个操作,插入、查找和删除。一般情况下Trie树中很少存在删除单独某个结点的情况,因此只考虑删除整棵树。

1.插入

  假设存在字符串str,Trie树的根结点为root。i=0,p=root。

  1)取str[i],判断p->next[str[i]-97]是否为空,若为空,则建立结点temp,并将p->next[str[i]-97]指向temp,然后p指向temp;

   若不为空,则p=p->next[str[i]-97];

  2)i++,继续取str[i],循环1)中的操作,直到遇到结束符'\0',此时将当前结点p中的isStr置为true。

2.查找

  假设要查找的字符串为str,Trie树的根结点为root,i=0,p=root

  1)取str[i],判断判断p->next[str[i]-97]是否为空,若为空,则返回false;若不为空,则p=p->next[str[i]-97],继续取字符。

  2)重复1)中的操作直到遇到结束符'\0',若当前结点p不为空并且isStr为true,则返回true,否则返回false。

3.删除

  删除可以以递归的形式进行删除。


代码:

[cpp]  view plain copy
  1. /* Arby_Trie树  2012.10.04 8:04am*/  
  2.   
  3. #include "iostream"  
  4. using namespace std;  
  5. #define Max 26  
  6.   
  7. class Trie  
  8. {  
  9. private:  
  10.     typedef struct TrieNode  
  11.     {  
  12.         bool isStr;                       //标记该节点是否构成单词  
  13.         struct TrieNode *next[Max];  
  14.         TrieNode():isStr(false)  
  15.         {  
  16.             memset(next, NULL, sizeof(next));    //将next内存全部置为NULL  
  17.         }  
  18.     };  
  19.     TrieNode *root;  
  20. public:  
  21.     Trie();  
  22.     ~Trie();  
  23.     void insert(const char *s);  
  24.     void insert(TrieNode *root, const char *s);  
  25.   
  26.     int search(const char *s);  
  27.     int search(TrieNode *root, const char *s);  
  28.   
  29.     void del();  
  30.     void del(TrieNode *root);  
  31. };  
  32.   
  33. Trie::Trie()  
  34. {  
  35.     root = new TrieNode();  
  36. }  
  37.   
  38. Trie::~Trie()  
  39. {  
  40.       
  41. }  
  42.   
  43. void Trie::insert(const char *s)  
  44. {  
  45.     insert(root, s);  
  46. }  
  47. void Trie::insert(TrieNode *root, const char *s)  
  48. {  
  49.     int i = 0;  
  50.     TrieNode *p = root;  
  51.     while(s[i] != '\0')  
  52.     {  
  53.         if(p->next[s[i] - 'a'] == NULL)  
  54.         {  
  55.             TrieNode *temp = new TrieNode();  
  56.             p->next[s[i] - 'a'] = temp;  
  57.             p = p->next[s[i] - 'a'];  
  58.         }  
  59.         else  
  60.         {  
  61.             p = p->next[s[i] - 'a'];  
  62.         }  
  63.         i++;  
  64.     }  
  65.     p->isStr = true;   //到达尾部,标记一个串  
  66. }  
  67.   
  68. int Trie::search(const char *s)  
  69. {  
  70.     return search(root, s);  
  71. }  
  72.   
  73. int Trie::search(TrieNode *root, const char *s)  
  74. {  
  75.     if(root == NULL)  
  76.         return false;  
  77.     TrieNode *p = root;  
  78.     int i = 0;  
  79.     while(s[i] != '\0')  
  80.     {  
  81.         if(p->next[s[i] - 'a'] == NULL)  
  82.             return false;  
  83.         else  
  84.         {  
  85.             p = p->next[s[i] - 'a'];  
  86.         }  
  87.         i++;  
  88.     }  
  89.     return (p != NULL && p->isStr == true);  
  90. }  
  91.   
  92. void Trie::del()  
  93. {  
  94.     del(root);  
  95. }  
  96. void Trie::del(Trie::TrieNode *root)  
  97. {  
  98.     if(root->isStr == false)  
  99.     for(int i = 0; i < Max; i++)  
  100.     {  
  101.         if(root->next[i] != NULL)  
  102.             del(root->next[i]);  
  103.     }  
  104.     delete root;  
  105. }  
  106.   
  107. //simple测试  
  108. int main()  
  109. {  
  110.     Trie TrieTree;  
  111.     char *s = "hello";  
  112.     char *s1 = "aa";  
  113.     TrieTree.insert(s);  
  114.     TrieTree.insert(s1);  
  115.     int test = TrieTree.search(s);  
  116.     cout << test << endl;  
  117.     test = TrieTree.search("bb");  
  118.     cout << test << endl;  
  119.     TrieTree.del();  
  120.   
  121.     return 0;  
  122. }  


Trie树的练习,poj3630题

Phone List
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 17042 Accepted: 5434

Description

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogue listed these numbers:

  • Emergency 911
  • Alice 97 625 999
  • Bob 91 12 54 26

In this case, it's not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob's phone number. So this list would not be consistent.

Input

The first line of input gives a single integer, 1 ≤ t ≤ 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 ≤ n ≤ 10000. Then follows nlines with one unique phone number on each line. A phone number is a sequence of at most ten digits.

Output

For each test case, output "YES" if the list is consistent, or "NO" otherwise.

Sample Input

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

Sample Output

NO
YES

题目大意:查找相同的前缀,但是不能是已经完结的输入号码,正好用字典树进行insert检查:

[cpp]  view plain copy
  1. #include "iostream"  
  2. #include "windows.h"  
  3.   
  4. using namespace std;  
  5. #define Max 10  
  6. int nodeNum;  
  7. int cases, count;  
  8.   
  9. typedef struct TrieNode  
  10.     {  
  11.         bool isStr;                       //标记该节点是否构成单词  
  12.         TrieNode *next[Max];       //每个节点存储所要的一个号码串  
  13.         //TrieNode():isStr(false)  
  14.         //{  
  15.         //  memset(next, NULL, sizeof(next));    //将next内存全部置为NULL  
  16.         //}  
  17.     };  
  18.      TrieNode node[10000];  
  19.   
  20. class Trie  
  21. {  
  22. private:  
  23.     TrieNode root;  
  24. public:  
  25.     Trie();  
  26.     ~Trie();  
  27.      bool insert(char num[]);  
  28. };  
  29.   
  30. Trie::Trie()  
  31. {  
  32.     root = node[0];  
  33. }  
  34.   
  35. Trie::~Trie()  
  36. {  
  37.       
  38. }  
  39.   
  40. bool Trie::insert(char num[])  
  41. {  
  42.     int i = 0;  
  43.     TrieNode *p = &root;  
  44.     int len = strlen(num);  
  45.     while(num[i])  
  46.     {  
  47.         if(i == len-1 && p->next[num[i] - '0'] != NULL)  
  48.         {  
  49.             return false;  
  50.         }  
  51.         if(p->next[num[i] - '0'] == NULL)  //如果不存在的话,则建立新的节点  
  52.         {  
  53.             p->next[num[i] - '0'] = &node[nodeNum];  
  54.             node[nodeNum].isStr = false;         //初始化  
  55.             memset(node[nodeNum].next,NULL,sizeof(node[nodeNum].next));   //这个很重要  
  56.             nodeNum++;  
  57.         }  
  58.         if(p->next[num[i] - '0']->isStr == true)  //到达一串数字的结尾了  
  59.         {  
  60.             return false;  
  61.         }  
  62.         p = p->next[num[i] - '0'];  
  63.         i++;  
  64.     }  
  65.     p->isStr = true;   //到达尾部,标记一个串  
  66.     return true;  
  67. }  
  68.   
  69.   
  70. //simple测试  
  71. int main()  
  72. {  
  73.     cin >> cases;  
  74.     while(cases--)  
  75.     {  
  76.         nodeNum = 1;  
  77.         bool flage = true;  
  78.         cin >> count;  
  79.         cin.get();  
  80.         char tel[11];  
  81.         Trie t;  
  82.         while(count--)  
  83.         {  
  84.             cin.get(tel, 11).get();  
  85.             //cin.get();  
  86.             if(!t.insert(tel))  
  87.             {  
  88.                 flage = false;  
  89.             }  
  90.         }  
  91.   
  92.         if(flage)  
  93.             cout << "YES" <<endl;  
  94.         else  
  95.             cout << "NO" << endl;  
  96.     }  
  97.   
  98.     return 0;  
  99. }  

存在问题 结构体创建如果放在类内,需要建一个构造函数,对结构体成员初始化,但是这样用的内存为栈内存,比较小,而我这次需要的是100000的结构体数组,这样的话,还未运行栈已经溢出了,因此需要将结构体在类外定义,并且结构体数组变为全局变量,这样就可以进行很好堆内存使用了。





遗留问题将结构体定义在类内,将数组变为static,这样进行实现,希望有志之士指点。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值