直到今天,我才知道原来字典树也是数据结构
字典树(Trie)是一种用于实现字符串快速存储和检索的多叉树结构。Trie的每个节点都拥有若干个字符指针(可理解为若干条单个字符的边),若在插入或检索字符串时扫描到一个字符c,就沿着当前节点的c字符指针,走向该指针指向的节点。
不好理解?
上图就展示了一棵字典树,其中的每一条与根节点相连的链都是一个字符串,可以发现,我们利用各个字符串之间的相同前缀,而不是每一个字符串都去开一个数组,大大减小了空间复杂度,同时使检索字符更加方便。
接下来,我向大家介绍字典树的两种基本操作。
初始化
首先,要初始化。。。
一颗空的字典树中仅包含一个节点,且该节点的所有字符指针指向空(如果字符串中只有小写字母,字符指针就有26个)。
int Trie[100005][26+5]={
}; //初始化直接全部指向空
int tot=1; //tot为当前节点编号
操作1:添加
如何在字典树中添加一个字符?
当需要插入一个字符串S时,我们令一个指针P起初指向根节点(可以理解为以根节点为起点,做好扫描准备),然后依次遍历S中的每一个字符c:
- 若P的c字符指针指向一个已经存在的节点Q,则令P=Q。
- 若P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q。
当S中的字符扫描完毕时,在当前节点P上标记它是一个字符串的末尾。
下图则是该操作的具体事例。
代码如下:
void Insert(){
char s[1005];
scanf("%s",s+1);
int len=strlen(s+1); //输入字符串
int p=1; //p为当前节点编号,一开始均指向根节点
for(int i=1;i<=len;i++){
if(!Trie[p][s[i]-'a']){
Trie[p][s[i]-'a']=++tot; //如果当前的字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q。
}
p=Trie[p][s[i]-'a']; //进入下一个节点。
}
End[p]=1; //标记以当前节点结尾的单词
}
操作2:查找
当需要检索一个字符串S在Trie中是否存在时,我们令一个指针P起初指向根节点,然后依次遍历S中的每个字符c:
- 若P的c字符指针指向空,则说明S没有被插入过Trie中,return 0;结束检索。
- 若P的c字符指针指向一个已经存在的节点Q,则令P=Q。
当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明S在Trie中存在,否则说明S没有被插入过Trie
直接放代码吧。。。
bool Find(* char s){
//读入一个字符串
int p=1;
int len=strlen(s+1);
for(int i=1;i<=len;i++){
if(!Trie[p][s[i]-'a']) //如果指针指向空,说明没有该字符串
return 0;
p=Trie[p][s[i]-'a']; //否自继续找
}
return End[p]; //判断是否结尾
}