字典树
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高,缺点是内存开销大。
字典树与字典很相似,当你要查一个单词是不是在字典树中,首先看单词的第一个字母是不是在字典的第一层,如果不在,说明字典树里没有该单词,如果在就在该字母的孩子节点里找是不是有单词的第二个字母,没有说明没有该单词,有的话用同样的方法继续查找.字典树不仅可以用来储存字母,也可以储存数字等其它数据。
c语言实现
#include <stdio.h> #include <string.h> #define NodeSize 100 //100000 #define CHARSIZE 26 //26 int flag[NodeSize]={0};//是否以该节点结尾,大小为节点数量 int tree[NodeSize][CHARSIZE];//每一个节点都有26个子节点,如果有大写字母则为52,加上数字就是62 int cnt=0;//记录已经存储字母的节点数量 void Insert(char s[]) { int root=0; int i; //遍历 for(i=0;s[i];i++) { //得到0-25范围的整数,代表a-z int id=s[i]-'a'; //判断该节点是否被赋值 //如果为-1,说明该节点还未存储字母,就需要创建新节点 if(tree[root][id]==-1) { //创建一个新节点 tree[root][id]=++cnt; } //如果存在直接指向这个节点,如果不存在,指向创建的新节点 //指向的是下标 root=tree[root][id]; } //设置该节点的结束标志 flag[root]= 1; } int Search(char s[]) { int root=0; int i; for(i=0;s[i];i++) { int id=s[i]-'a'; if(tree[root][id]==-1) return 0; root=tree[root][id]; } //判断该节点是否为结束标志位 if(flag[root]){ return 1; }else{ return 0; } } void Init(){ int i; int j; for(i=0;i<NodeSize;i++){ for(j=0;j<CHARSIZE;j++){ tree[i][j] = -1; } } } int main() { Init(); char s1[5] = "fggf"; char s2[4] = "fbs"; char s3[6] = "fb"; //插入字符串 Insert(s1); //插入字符串 Insert(s2); //查找字符串 int flag = Search(s3); if(flag){ printf("字符串存在"); }else{ printf("字符串不存在"); } return 0; }
基本操作
插入:在字典树中插入单词
1.每次插入从根节点开始
int root=0;
2.遍历单词
for(i=0;s[i];i++) { 。。。 }
3.得到当前字母在此节点下的位置,每个节点有26个子节点。
int id=s[i]-'a';
4.判断该节点是否已经存储字母,如果存储,直接指向该节点,如果没有,新增节点,并指向新增节点。
//判断该节点是否被赋值 //如果为-1,说明该节点还未存储字母,就需要创建新节点 if(tree[root][id]==-1) { //创建一个新节点 tree[root][id]=++cnt; } //如果存在直接指向这个节点,如果不存在,指向创建的新节点 //指向的是下标 root=tree[root][id];
5.结束插入后,在此节点的位置标记单词结束标志。
//设置该节点的结束标志 flag[root]= 1;
void Insert(char s[]) { int root=0; int i; //遍历 for(i=0;s[i];i++) { //得到0-25范围的整数,代表a-z int id=s[i]-'a'; //判断该节点是否被赋值 //如果为-1,说明该节点还未存储字母,就需要创建新节点 if(tree[root][id]==-1) { //创建一个新节点 tree[root][id]=++cnt; } //如果存在直接指向这个节点,如果不存在,指向创建的新节点 //指向的是下标 root=tree[root][id]; } //设置该节点的结束标志 flag[root]= 1; }
查找:在字典树中查找单词是否存在
int Search(char s[]) { int root=0; int i; for(i=0;s[i];i++) { int id=s[i]-'a'; if(tree[root][id]==-1) return 0; root=tree[root][id];; } //判断该节点是否为结束标志位 if(flag[root]){ return 1; }else{ return 0; } }
最重要的功能:已知前缀,查所有出现的单词。
void iter(int root){ int j; for(j=0;j<26;j++){ //tree[root][j]存储的是节点位置 if(tree[root][j] != -1){ printf("%d:%c ",root,j+'a'); //迭代: iter(tree[root][j]); printf(" "); } } } int locate(char s[]) { int root=0; int i; for(i=0;s[i];i++) { int id=s[i]-'a'; if(tree[root][id]==-1) return 0; root=tree[root][id]; } //判断该节点是否为结束标志位 if(flag[root]){ return 0; } iter(root); return 1; }
#include <stdio.h> #include <string.h> #define NodeSize 100 //100000 #define CHARSIZE 26 //26 int flag[NodeSize]={0};//是否以该节点结尾,大小为节点数量 int tree[NodeSize][CHARSIZE];//每一个节点都有26个子节点,如果有大写字母则为52,加上数字就是62 int cnt=0;//记录已经存储字母的节点数量 void Insert(char s[]) { int root=0; int i; //遍历 for(i=0;s[i];i++) { //得到0-25范围的整数,代表a-z int id=s[i]-'a'; //判断该节点是否被赋值 //如果为-1,说明该节点还未存储字母,就需要创建新节点 if(tree[root][id]==-1) { //创建一个新节点 tree[root][id]=++cnt; } //如果存在直接指向这个节点,如果不存在,指向创建的新节点 //指向的是下标 root=tree[root][id]; } //设置该节点的结束标志 flag[root]= 1; } int Search(char s[]) { int root=0; int i; for(i=0;s[i];i++) { int id=s[i]-'a'; if(tree[root][id]==-1) return 0; root=tree[root][id]; } //判断该节点是否为结束标志位 if(flag[root]){ return 1; }else{ return 0; } } void iter(int root){ int j; for(j=0;j<26;j++){ //tree[root][j]存储的是节点位置 if(tree[root][j] != -1){ printf("%d:%c ",root,j+'a'); //迭代: iter(tree[root][j]); printf(" "); } } } int locate(char s[]) { int root=0; int i; for(i=0;s[i];i++) { int id=s[i]-'a'; if(tree[root][id]==-1) return 0; root=tree[root][id]; } //判断该节点是否为结束标志位 if(flag[root]){ return 0; } iter(root); return 1; } void Init(){ int i; int j; for(i=0;i<NodeSize;i++){ for(j=0;j<CHARSIZE;j++){ tree[i][j] = -1; } } } int main() { Init(); char s1[5] = "fggf"; char s2[4] = "fbs"; char s3[6] = "f"; //插入字符串 Insert(s1); //插入字符串 Insert(s2); //查找字符串 locate(s3); return 0; }
更多阅读: