字典树(trie 树)

字典树(trie树)

  字典树是一种在字符串查找,前缀匹配等方面应用广泛的算法,它在查找字符串时只与被查询的字符串长度有关,所以它在查找时只有O(1)的时间复杂度,但随之而来的较大的空间复杂度。

一.原理分析

  如图,字典树的每一个节点是由一个数据域(用来标记是否在此处有字符串终止)与26个指针域(表示26个小写字母)组成(PS:联想链表)。每个节点表示一个字符,我们将我们将要输入的字符串插入字典树中,从根节点到某一节点(具有终止标记,上图红点),为已插入字符串,上图中的字符串有:abc、abcd、abd、b、bcd、efg、hig(图片转载字百度图片)。

二.分步实现

1.trie树结构体定义

 1 struct node
 2 {
 3     bool k;
 4     node *next[26];    //定义的字典树为只有26个小写字母,可增加减少
5 node() 6 { 7 int i; 8 for(i=0; i<26; i++) 9 next[i] = NULL; 10 k = false; 11 } 12 ~node() 13 { 14 int i; 15 for(i=0;i<26;i++) 16 { 17 if(next[i]!=NULL) 18 { 19 delete next[i]; 20 } 21 } 22 } 23 }; 24 node *head;

 

PS:被注释掉部分是用构造函数初始化节点与使用析构函数删除节点。
  上图代码中的next[26]表示每个节点的下一层节点数量,小写字母为26,加上大写就是52,加上数字就是62,以此类推。而bool型的K则表示该位置是否有终止标记,可根据题意改变为其他标记。

2.字典树的插入

 1 void insert_ch(char *ch)
 2 {
 3     int i;
 4     node *p=head;
 5     for(i=0; ch[i]; i++)
 6     {
 7         if(p->next[ch[i]-'a'] == NULL)          //判断下层节点是否存在
 8             p->next[ch[i]-'a'] = new node;  //开辟新空间
 9         p = p->next[ch[i]-'a'];                    //向下一层进行拓展
10     } 
11     p -> k = true;                                     //进行字符串结尾标记
12 }

  每次从根节点进行插入,如果向下的节点已经存在,就直接读取,否则拓展一个新节点。之后将最后一个节点的k标记为true表示该位置有一个字符串结尾。

3.字符串查找

 1 bool find_ch(char *ch)
 2 {
 3     int i;
 4     node *p=head;
 5     for(i=0; ch[i]; i++)
 6     {
 7         if(p->next[ch[i]-'a']==NULL)
 8             return false;
 9         p = p -> next[ch[i]-'a'];
10     }
11     return p -> k;
12 }

  基本过程与插入相同,向下查找,入过该节点不存在,直接返回false,如果存在一直向下查找,最终返回末尾标记的k。

1.模板代码

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 struct node
 8 {
 9     bool k;
10     node *next[26];
11     node()
12     {
13         int i;
14         for(i=0;i<26;i++)
15         {
16             next[i]=NULL;
17             k=false;
18         }
19     }
20     ~node()
21     {
22         int i;
23         for(i=0;i<26;i++)
24         {
25             if(next[i] != NULL)
26                 delete next[i];
27         }
28 
29     }
30 };
31 node *head;
32 void insert_ch(char *ch)
33 {
34     int i;
35     node *p = head;
36     for(i=0;ch[i];i++)
37     {
38         if(p -> next[ch[i]-'a' ] == NULL)
39             p -> next[ch[i]-'a' ] = new node;
40         p = p -> next[ch[i]-'a' ];
41     }
42     p -> k = true;
43 }
44 bool find_ch(char *ch)
45 {
46     int i;
47     node *p = head;
48     for(i=0;ch[i];i++)
49     {
50         if(p -> next[ch[i]-'a' ] == NULL)
51             return false;
52         p = p -> next[ch[i]-'a' ];
53     }
54     return p -> k;
55 }
56 char ch[100];
57 int main()
58 {
59     head = new node;
60     while(~scanf("%s",ch))
61         insert_ch(ch);
62     while(~scanf("%s",ch))
63         printf("%s\n",find_ch(ch) ? "YES" : "NO");
64     delete head;                                     //删除节点
65     return 0;
66 }

 

三.另一种实现方式

  动态分配内存对空间掌控很好,用一个节点开辟一个节点,但是在做题中有时会出现大大小小的问题不好结决,所以可以选择另一种方法,直接开辟出一个很大的结构体数组用来保存节点,不用考虑开辟与删除节点问题。

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 struct node
 8 {
 9     bool k;
10     node *next[26];
11 };
12 node no[10000000],*head;
13 int iii;
14 node *new_node()
15 {
16     int i;
17     node *p=&no[iii++];
18     for(i=0;i<26;i++)
19     {
20         p -> next[i] = NULL;
21     }
22     p -> k = false;
23 }
24 void insert_ch(char *ch)
25 {
26     int i;
27     node *p=head,*t;
28     for(i=0; ch[i] ;i++ )
29     {
30         if(p->next[ch[i]-'a']==NULL)
31         {
32             t=new_node();
33             p->next[ch[i]-'a']=t;
34         }
35         p = p->next[ch[i]-'a'];
36     }
37     p -> k = true;
38 }
39 bool find_ch(char ch[])
40 {
41     int i;
42     node *p=head;
43     for(i=0;i<ch[i];i++)
44     {
45         if(p->next[ch[i]-'a']==NULL)
46             return false;
47         p = p -> next[ch[i]-'a'];
48     }
49     return p -> k;
50 }
51 int main()
52 {
53     char s[100];
54     int i,n;
55     head = new_node();
56     while(~scanf("%d",&n))
57     {
58         iii=0;
59         for(i=0;i<n;i++)
60         {
61             scanf("%s",s);
62             insert_ch(s);
63         }
64         scanf("%d",&n);
65         for(i=0;i<n;i++)
66         {
67             scanf("%s",s);
68             printf("%s\n",find_ch(s) ? "YES" : "NO");
69         }
70     }
71     return 0;
72 }

  这种方法与上面的基本相同,只是节点不在用new开辟,而是直接从结构体数组中取用,这种方法的弊端节点有最大值,当需要保存的数据量过大时,会数组越界。保存数据量小时对空间的浪费也很严重。

四.相关基础题目

HDOJ 1251 统计难题:
http://acm.hdu.edu.cn/showproblem.php?pid=1251

HDOJ 1671 Phone List:
http://acm.hdu.edu.cn/showproblem.php?pid=1671    //用第一种方法记得释放空间,否则超内存

HDOJ 1247 Hat’s Words:

http://acm.hdu.edu.cn/showproblem.php?pid=1247

 

 

PS:本贴纯手打,如打码有问题请各位大牛指正,谢谢大家~

欢迎大家转载哦~请于此处注明出处:http://www.cnblogs.com/zhuyuan/

posted @ 2015-02-07 11:47  起飞的菜鸟  阅读(2530)  评论(1编辑  收藏  举报