字典树 trieTree

来源:互联网 发布:周笔畅淘宝店服装 编辑:程序博客网 时间:2024/06/16 17:56

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;}

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

0 0
原创粉丝点击