原文:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28977986&id=3807947
1、什么是Trie树
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
1.根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2.从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3.每个节点的所有子节点包含的字符都不相同。
2、Trie树的构建
本质上,Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。举一个例子。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:
搭建Trie的基本算法很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:
1.考察前缀"a",发现边a已经存在。于是顺着边a走到节点a。
2.考察剩下的字符串"dd"的前缀"d",发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad
3.考察最后一个字符"d",这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
具体Trie树的创建、插入、查询代码如下所示:
本质上,Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。举一个例子。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:
搭建Trie的基本算法很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:
1.考察前缀"a",发现边a已经存在。于是顺着边a走到节点a。
2.考察剩下的字符串"dd"的前缀"d",发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad
3.考察最后一个字符"d",这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
具体Trie树的创建、插入、查询代码如下所示:
1 //此函数只考虑26个英文字母的情况 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 6 #define MAX_CHILD 26 7 8 9 typedef struct Tree 10 { 11 int count; //用来标记该节点是个可以形成一个单词,如果count!=0,则从根节点到该节点的路径可以形成一个单词 12 struct Tree *child[MAX_CHILD]; 13 }Node,*Trie_node; 14 15 16 Node* CreateTrie() //创建trie节点树 17 { 18 Node *node=(Node*)malloc(sizeof(Node)); 19 memset(node,0,sizeof(Node)); 20 return node; 21 } 22 23 24 25 void insert_node(Trie_node root,char *str) //trie树插入结点 26 { 27 if(root ==NULL || *str=='\0') 28 return; 29 Node *t=root; 30 31 char *p=str; 32 33 while(*p!='\0') 34 { 35 if(t->child[*p-'a']==NULL) 36 { 37 Node *tmp=CreateTrie(); 38 t->child[*p-'a']=tmp; 39 } 40 t=t->child[*p-'a']; 41 p++; 42 } 43 t->count++; 44 } 45 46 47 void search_str(Trie_node root,char *str) //查找串是否在该trie树中 48 { 49 if(NULL==root || *str=='\0') 50 { 51 printf("trie is empty or str is null\n"); 52 return; 53 } 54 55 char *p=str; 56 Node *t=root; 57 while(*p!='\0') 58 { 59 if(t->child[*p-'a']!=NULL) 60 { 61 t=t->child[*p-'a']; 62 p++; 63 } 64 else 65 break; 66 } 67 if(*p=='\0') 68 { 69 if(t->count==0) 70 cout<<"该字符串不在trie树中,但该串是某个单词的前缀\n"; 71 else 72 cout<<"该字符串在该trie树中\n"; 73 } 74 else 75 cout<<"该字符串不在trie树中\n"; 76 } 77 78 79 void del(Trie_node root) //释放整个字典树占的堆空间 80 { 81 int i; 82 for(i=0;i<MAX_CHILD;i++) 83 { 84 if(root->child[i]!=NULL) 85 del(root->child[i]); 86 } 87 free(root); 88 } 89 90 91 92 93 int main() 94 { 95 int i,n; 96 char str[20]; 97 cout<<"请输入要创建的trie树的大小:"; 98 cin>>n; 99 Trie_node root=NULL; 100 root=CreateTrie(); 101 if(root==NULL) 102 cout<<"创建trie树失败"; 103 for(i=0;i<n;i++) 104 { 105 scanf("%s",str); 106 insert_node(root,str); 107 } 108 cout<<"trie树已建立完成\n"; 109 cout<<"请输入要查询的字符串:"; 110 while(scanf("%s",str)!=NULL) 111 { 112 search_str(root,str); 113 114 } 115 return 0; 116 117 118 }