trie树又被称为前缀树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串,所以经常被搜索引擎系统用于文本词频统计。
它的3个基本性质:
- 根节点不包括字符,除根节点外每个节点都只包含一个字符
- 从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串
- 每个节点的所有子节点包含的字符都不相同
树的构建
假设有 b,abc,abd,bcd,abcd,efg,hil这6个单词,建树的过程如下:
对于每一个节点,从根节点到它的过程就是一个单词,如果这个节点标记为已存在,就表示这个单词已经出现过,可以设计一个计数器count记录该单词出现的次数。对于一个单词,我们只要顺着根走到对应的节点,再看看这个节点是否存在,就可以知道它是否出现过。把这个节点标记为已存在,相当于插入这个单词。这样可以达到查询和插入是一起完成的。(实际上就是一个26叉树,每个节点里是26大小的字符数组)
代码实现:
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;
#define MAX 26
typedef struct TireNode
{
bool isstr;//是否存在
int count;//计数器表示出现次数
TireNode* next[MAX];///下一个节点
}Trie;
Trie* Buynode()
{
Trie* s=(Trie*)malloc(sizeof(Trie));
if(s==NULL) exit(1);
for(int i=0;i<MAX;i++)
{
s->next[i]=NULL;
}
return s;
}
void Freenode(Trie* root)
{
free(root);
}
void Insert(Trie* root,const char* s)
{
if(root==NULL || *s=='\0')
{
return ;
}
int i=0;
Trie *p=root;
while(*s!='\0')
{
if(p->next[*s-'a']==NULL)//之前没有出现过,就创建新节点
{
Trie* tmp=Buynode();
tmp->isstr=false;
tmp->count=0;
p->next[*s-'a']=tmp;
p=p->next[*s-'a'];
}
else
{
p=p->next[*s-'a'];
}
s++;
}
p->isstr=true;//将该词该称已存在
p->count++;//计数器+1
}
typedef struct
{
bool is;
int count;
}Res;
Res Search(Trie* root,const char* s)
{
Res res={false,0};
Trie* p=root;
while(p!=NULL && *s!='\0')
{
p=p->next[*s-'a'];
s++;
}
if(p!=NULL && p->isstr==true)
{
res.is=true;
res.count=p->count;
}
return res;
}
int main()
{
const char* a[]={"data","date","eye","data","hello","world","data","date","eye",
"data","hello","world","data","date","eye","data","hello","world","data"};
int len=sizeof(a)/sizeof(a[0]);
Trie* root=Buynode();
root->isstr=false;
root->count=0;
for(int i=0;i<len;i++)
{
Insert(root,a[i]);
}
Res res=Search(root,"data");
cout<<res.is<<" "<<res.count<<endl;
Freenode(root);
return 0;
}
运行结果: