字典树 trieTree

Tire树
即字典树,又称单词查找树,利用字符串的公共前缀,省去很多重复的比较,从而节省查询时间。
经常被搜索引擎系统用于文本词频统计。
这里写图片描述

上图便是一颗字典树,
可以看到,该树有以下特点:
树的根节点不含字符,其余节点含且只含一个字符
兄弟间所含字符各不相同
红色表示在该点形成一个单词(从根节点到该节点路径上所有字符连成的字符串)
例如,abc、abcd、abd等都是单词,上图共有7个红色节点,故有7个单词。
ab并不是单词,只是某些单词的前缀。

假设我们要查询abc这个单词,首先要查询根节点的子节点中含不含a字符,如果不包含,则说明不包含abc这个单词。

如果根节点的子节点中包含a字符,那么我们只需从a节点开始查找,不必考虑第二层的b、e、h结点,如果a节点的子节点中不含b字符,则说明不包含abc这个单词,如果含b字符,则继续判断b字符的子节点中有没有c字符…

如果到了单词的最后一个字母,还需判断该节点“是否为红色”,如果是红色,则该字典树中存在这个单词。

可以看到,查询一个长度为n的单词,时间复杂度仅仅为O(n)。

至于字典树的构建,与查询的算法有些相仿。

例如,要在一颗字典树中插入单词efh,先看根节点的子节点中有没有e,如果没有,则为根节点创建一个子节点e,然后继续为e节点创建一个子节点f,然后……

如果根节点的子节点中有e,则继续看e节点的子节点中有没有f,无则创建,有则继续查询……

最后,插入或查询到最后一个字母,需把该节点“标志为红色”,表明在该节点形成一个单词。

#include<stdio.h>
struct trieTree{
    bool isstr;
    //isstr为true表明该节点处形成单词,也就是上文的“红色”
    trieTree* next[26];
    //每个节点有26个子节点,不过还未赋“字符”
};
trieTree* _init(){
//节点初始化
    trieTree* r = new trieTree;//申请空间
    r->isstr = false;//该节点不形成单词
    for(int i=0;i<26;i++)
        r->next[i] = NULL;//为每个子节点赋值为空
    return r;
}
void _insert(char* str,trieTree* root){
//插入单词,str是待插入单词的首字母的地址
    trieTree* tmp = root;
    while(*str!='\0'){//字符串的结束字符是'\0'
        if(!tmp->next[*str-'a']){
            //next[i]中,i范围是0到25,分别代表字符a到z,
            //如果*str=b,那么next[*str-'a'],
            //即next[1],表示tmp节点的b子节点
            //!tmp->next[*str-'a'] 指:tmp不存在b子节点
            trieTree* r = _init();//创建结点
            tmp->next[*str-'a'] = r;
            tmp = r;
        }
        else
            tmp = tmp->next[*str-'a'];
            //如果tmp存在(*str)节点,使tmp指向此节点
        str++;//str指向下一个字母的地址
    }
    tmp->isstr = true;//“标记为红色”
}
bool _search(char* str,trieTree* root){
//查询单词,返回true表示待查询单词存在于字典树中,返回false表示不存在
    trieTree* tmp = root;
    while(*str!='\0'){
        if(!tmp->next[*str-'a']){
            return false;
            //如果找不到(*str),说明字典树中不存在此单词,返回false
        }
        else
            tmp = tmp->next[*str-'a'];
        str++;
    }
    return tmp->isstr;
    //待查询单词的所有字母都找到了,还需判断最后一个单词结点是否为“红色”
}
void _del(trieTree* root){
//释放内存
    for(int i=0;i<26;i++)
        if(root->next[i])
            _del(root->next[i]);//递归
    delete root;
}
int main(){
    trieTree* root = _init();
    char str[4][10] = {"hello","world","software","engineer"};
    for(int i=0;i<4;i++)
        _insert(str[i],root);//插入单词
    char str1[10];
    while(scanf("%s",str1),str1[0]!='#'){//输入'#'开头字符串结束输入
        int ans = _search(str1,root);
        puts((ans?"yes":"no"));//如果ans==1输出yes,否则输出no
    }
    _del(root);
    return 0;
}

当然,字典树的功能不止于查询某单词是否存在,经过适当修改,可以查询“以某字符串为前缀的单词个数”、“一组单词中某单词出现的次数”等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值