字典树(Trie)——入门篇

字典树

Trie(字典树)又称前缀树

给出字符串集合:code、cook、five、file、fat,我们设根节点为空节点,那么其Trie树如下所示:
在这里插入图片描述

字典树又称前缀树,给出一个字符串集合,我们可以通过公共前缀将整个字符串集合存到一个树形结构中,一旦形成了集合中的字符串,我们只需在树上的相应节点打上标记

存储

我们采用双数组存储的方式,其中Next数组是二维数组,第二维之所以是26是因为每个节点都可能连接26个字母,另外一个标记数组可以有两种形式:

  • bool类型的vis数组只标记该节点结尾字符串是否存在
  • int类型的num数组既可以标记是否存在又能存储在集合中的下标
const int maxn=max_len*num;   //字符串最大长度乘以数量
int Next[maxn][26];
bool vis[maxn];     //该结点结尾的字符串是否存在
int num[maxn];	    //该结点结尾的字符串是否存在,存在则保存下标

不难发现我们在存储时是将字符转化为0-25的数字下标:
在这里插入图片描述

建树

如何建树?我们采用动态开点的方式,这里类似链式前向星那样。Next数组第一维保存的就是动态开点的数,第二维是接下来指向的字符(0-25)

每个字符串的第一个字符保存在下标为0的集合中,而且root实际上是不存在的,也就是说如果有多种字母开头的字典树,那么这实际上是一个森林

建树的过程即插入的过程,时间复杂度为O(n*len)

标记是否存在

int cnt=0;
void insert(char *s) {    //插入字符串
        int p=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int c=s[i]-'a';
            if(!Next[p][c]) Next[p][c]=++cnt;  //没有就添加结点
            p=Next[p][c];
        }
        vis[p]=1;
    }

标记并保存下标

int cnt=0;
void insert(char *s,int k) {  //插入字符串集合的字符串k
        int p=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int c=s[i]-'a';
            if(!Next[p][c]) Next[p][c]=++cnt;  //没有就添加结点
            p=Next[p][c];
        }
        num[p]=k;
    }
查找字符串

查找时,首先目标串的每一个前缀必须都出现,也就是Next[p][s[i]-‘a’]都有值,那么查询到最后一个节点时,如果该节点被标记,那么vis[p]就不为0,否则为0,因此直接返回vis[p]

查询的时间复杂度为O(len)

int find(char *s){      //查找字符串
        int p=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int c=s[i]-'a';
            if(!Next[p][c]) return 0;
            p=Next[p][c];
        }
        return vis[p];
    }
简单模板
const int maxn=max_len*num; //每个字符串最大长度乘以字符串数量
struct Trie{
    int Next[maxn][26],cnt;
    bool vis[maxn];           //该结点结尾的字符串是否存在
    //int num[maxn];

    void init(){
        cnt=0;
        memset(vis,0,sizeof vis);
        memset(Next,0,sizeof Next);
    }

    void insert(char *s,int k) {    //插入字符串
        int p=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int c=s[i]-'a';
            if(!Next[p][c]) Next[p][c]=++cnt;  //没有就添加结点
            p=Next[p][c];
        }
        vis[p]=1;
    }

    int find(char *s){    // 查找字符串
        int p=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int c=s[i]-'a';
            if(!Next[p][c]) return 0;
            p=Next[p][c];
        }
        return vis[p];
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值