目录
一、概述
Trie树,即字典树,又称前缀树、单词查找树或键树。
Trie树的典型应用是保存大量的字符串(但不限于字符串),并快速排序、统计、查找字符串或前缀。
Trie树中的根节点表示空字符串,结点表示字符串前缀或字符串,边表示字符。
二、节点设计
struct Node1{//方式一
int c;
int p[26];//数组范围取决于题意
}trie[N];
struct Node2{//方式二
int c;
map<char,int>p;
}trie[N];
方式一速度快,但是空间浪费大,适合组成字符串的不同字符数量较少的情况;方式二适应范围更广,但是索引速度慢,适合组成字符串的不同字符数量较多的情况。
三、插入字符串
Trie树中初始拥有一个根节点(trie[1]),表示空串,体现在代码中是“int cnt=1;”。
插入字符串时,从根节点开始,按串中字符逐一匹配Trie树中的边,转移结点,如果当前节点没有对应字符边可匹配,则当前结点以该字符边连接新结点,再转移;如果按串中字符转移完毕,当前结点c值加1。
struct Node{
int c;
map<char,int>p;
}trie[N];
int cnt=1;
int insert(char *s){
int i=0,u=1;
while(s[i]){
if(trie[u].p.count(s[i])){
u=trie[u].p[s[i++]];
}
else{
u=trie[u].p[s[i++]]=++cnt;
}
}
return ++trie[u].c;
}
四、判断前缀
在当前Trie树中判断是否有字符串s为前缀的字符串,只需要在Trie树中转移s,如果能够将s中的字符逐一转移完毕,则说明Trie树中一定存在字符串是以s为前缀的。
bool isPrefix(char *s){
int i=0,u=1;
while(s[i]){
if(trie[u].p.count(s[i])){
u=trie[u].p[s[i++]];
}
else{
return 0;
}
}
return 1;
}
五、删除字符串
删除字符串有多种情况:
- 待删除字符串为叶子结点,该串在Trie树中不止一个,直接将c值减1即可。
- 待删除字符串为叶子结点,该串在Trie树中只有一个,需要回溯并删除结点和边,直至其祖先结点c值不为0或还有其他子节点。
- 待删除字符串为叶子结点且唯一,但是该串在Trie树中和其他串有公共前缀,需要回溯并删除结点和边,直至其祖先结点c值不为0或还有其他子节点。
- 待删除字符串不是叶子结点,直接将c值减1即可。
bool del(char *s,int u){
char c=s[0];
if(!c){
if(trie[u].c){
trie[u].c--;
}
else{
return 0;
}
}
else if(trie[u].p.count(c)){
if(del(s+1,trie[u].p[c])){
trie[u].p.erase(c);
}
else{
return 0;
}
}
else{
return 0;
}
if(!trie[u].c&&!trie[u].p.size()){
return 1;
}
else{
return 0;
}
}