数据结构之前缀树(Trie)

前缀树,又叫字典树,主要用于字符串(不限于字符串)查询、统计、排序的一种数据结构

比如,给定n个字符串,进行m次查询,每次查询给定一个字符串 t,问t 是否存在于那给定的n个字符串里

这里,我们用到了前缀树,即将每个字符串看作一条链,把拥有相同前缀的字符串的链的相同前缀给合并,形成一棵棵子树。如给定三个字符串his,her,hit,得到前缀树:

上图很清晰了,在右边的树里面从根出发,一直到叶子结点,可以找到his,hit,her三个单词

那么问题来了,如果再来一个字符串it,怎么插入到这棵Trie里?显然这是无法接到根节点h下面的,因为it第一个字母不是h~~

那么我们可以令一个空结点为根,所有子树都从这个空结点出发,即:

这时又有了一个新的问题,it插进Trie了,如果再有一个its怎么办?直接在it后面加一个s,那么不就把it覆盖掉了吗?

这个好解决,只需要使用一个val[]数组记录每一个结点的权值即可,从根开始,向下查找,找到某个结点i,val[i]==0表示当前得到的字符串只是一个前缀,并不在我们之前插入到Trie里的众多字符串(后面称之为字典)里,不是0则说明这个字符串在字典里。额,我懒地画图了,反正这玩意儿也好理解。

然后就是实现了:使用0表示根节点(就是那个空结点)然后1,2,3......依次表示下面的结点,第一个插入到Trie里的为1(即id为1),第二个插入到Trie里的为2(即id为2),以此类推。使用二维数组ch[id][son]来表示Trie,ch[i][j]表示的是id为i对应结点的j儿子的id。至于ch[i][j]那个j怎么表示字符,就看情况而定了,比如如果只会出现小写字母,那就用0,1,2,....,25分别表示a,b,c...,z即可。

给出只有小写字母的的Trie代码:

struct Trie
{
    int ch[maxnode][sigma_size];//maxnode是结点总数,sigma_size是字母表的大小(即可能出现多少种字符,0~sigma_size与字符一一对应)
    int val[maxnode];
    int sz;//结点总数
    Trie(){ sz = 1; memset(ch[0], 0, sizeof ch[0]);}//起始只有根节点
    int idx(char c) {   return c-'a';} // 字符c的编号

    // 插入字符串s,附加信息为v(例如字符串的权值等),v非0,因为0表示本结点不是单词结点
    void insert(char* s, int v)
    {
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; ++ i)
        {
            int c = idx(s[i]);
            if(!ch[u][c])//结点不存在
            {
                memset(ch[sz], 0, sizeof ch[sz]);
                val[sz] = 0;
                ch[u][c] = sz ++;
            }
            u = ch[u][c];//往下走
        }
        val[u] = v;//字符串的最后一个字符的附加信息为v
    }

    // 查询字符串s,如果存在返回附加值,否则返回0
    int find(char* s)
    {
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; ++ i)
        {
            int c = idx(s[i]);
            if(!ch[u][c])   return 0;
            u = ch[u][c];
        }
        return val[u];
    }
    
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值