部分概念引自百度百科和各位神犇资料,侵删。
百度百科:字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。
就是这个:
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
字典树的基本性质:
1、根节点上没有字符,除根节点外,每个节点上有且只有一个字符;
2、每个节点所有的子节点字符都不相同;
3、某点所代表的字符串为从根节点走到该点,路上经过的一串字符串。
一、建树和查询
答疑的时候有棵比我还白菜的白菜问了一个问题:
插入akid时,查找aki时,会不会显示存在?
由于担心两天后我也会问出这样的问题。。。
嗯不会。
每插入一个单词会在结束的地方打上标记,查找时如果显示这个位置有标记,则这个单词存在。
到这里,我们就得到了一个包含a,aye,bad,bat,cc五个单词的字典树了。
查询和添加新字符串可以一起实现~
查询的时候,按照字符串的每一位从根开始走,如果走不到有标记的点,则字符串不存在。反之存在。
添加新字符串时,从根节点往下走,如果碰不到相应节点就添加一个,走到最后一位时,在最后一个节点上打上标记。
利用字典树,时间复杂度可以达到O(NL+ML)
由于字典树的度最大可以达到127(虽然一般也就26)而且深度可能会比较大。
我们不能使用顺序存储的方法,必须使用指针。
在现实编程中,我们通常使用数组模拟指针,也就是利用child[node][char]的形式来存储字典树。
child[1][a]=2
关于字典树的度,百科上没有对应解释,只找到这样一句:
如果字符集是26个字母,那每个节点的度就有26个,典型的以空间换时间结构。
凑合着理解吧。
二、代码实现
①插入
void add(char *wd)//wd为字符串
{
int i;
for(i=1;*wd;wd++)//遍历wd的每一个字符
{
int r=*wd-'a';//假设wd为小写字母构成的字符串 转化为对应的ascll码值
if(!child[i][r])//如果这个字母对应的节点不存在
{
child[i][r]=++size;//size为节点编号
fa[size]=i;//fa指向size号节点的上一个节点
}
i=child[i][r];//如果存在,继续向下遍历
}
cnt[i]=1;//标记为存在
}
②查询字符串存在性
void find_if(char *wd)
{
int i;
for(i=1;*wd;wd++)
i=child[i][*wd-'a'];
return cnt[i];
}
③查找并输出字符串在串集里面唯一确定的最短前缀
void find_shortest(char *wd)
{
int i;
for(int i=1;*wd;wd++)
{
int r=*wd-'a';
i=child[i][r];
printf("%c",*wd);
if(word[i]==1) return;//以该节点结尾的单词只有一个
}
}
④判断该字符串是不是串集里某个字符串前缀
bool judge(char *wd)
{
int i;
for(i=1;*wd;wd++)
{
int r=*wd-'a';
i=child[i][r];
}
if(word[i]==1) return true;
return false;
}
⑤判断字符串是否由串集里的两个字符串构成
这个不太懂,就只把板子搬过来。。
//假设该字符串分两部分,这里只实现一部分的查找
bool find(char *wd)
{
int i;
for(i=1;*wd;wd++)
{
int r=*wd-'a';
if(!child[i][r]) return false;//不存在节点
i=child[i][r];
}
return cnt[i];//最后一个是不是单词节点
}