Trie树简单讲解

trie树简单讲解

一.Trie树是什么

Trie树如下

trie树就是一棵树,根节点什么都不存,子节点存的不是数字,而是字母

如图所示,图中有三个单词:me,hi,how.

trie树可以方便地查找这些单词,也可以找出某个前缀出现的次数

二.如何建树

我们可以开一个数组

trie[i][j]=k

它的意思是编号为i的节点有一个编号为j的儿子,并且它代表的字母是k

我们用数字代表字母,那么a=0,b=1,c=2.....以此类推,k就是这些数字

 

三.插入单词

 

假设在这棵树中我们要插入一个单词

这样有两种情况:

1.它的某个字母已存在树中

2.它的某个字母不存在树中

于是我们举一个例子:

插入“house”

我们首先在根节点的儿子中找“h”,发现找到了,属于第一种情况

于是我们继续在“h”的儿子中找“o”,也找到了,还是属于第一种情况

接下来我们在“o”的儿子中找“u”,没有找到,属于第二种情况,这时候我们要再建一个“o”的子节点

...................(以此类推)

于是就插入完了:

具体代码如下:

void input(char a[12],int rt)//a字符串是要插入的字符串,rt是root,当前根节点编号 
{
    int len=strlen(a);//取a的长度 
    for(int i=0;i<=len-1;i++)//枚举a中每一个字符 
    {
        int x=a[i]-'a';//当前字母的数字编号 
        if(trie[rt][x]==0)//第二种情况,及树中不存在这个点 (第一种情况不用处理,直接看下一个)
        {
            trie[rt][x]=++idx;//新建一个点(idx是访问顺序) 
        }
        rt=trie[rt][x];    // 将根节点改为当前点,以便下次查找下一个点 
    }
}

四.查找单词

我们以查找前缀举例讲解:

假设我们查找前缀“mou”

首先我们在根节点的儿子m与h中找“mou”的首字母“m”,发现找到了

我们接下来在“m”的子节点中找“o”,发现找不到,于是我们可以直接断定找不到这个前缀啦

具体代码如下:

int search(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)//如果不存在这个点 
        {
            return 0;//直接返回 0(找不到) 
        }
        rt=trie[rt][x];//更新root 
    }
    return 1;//如果for循环结束了还没有返回,既没有找不到,返回找得到 
}

五.完整代码

第一种情况:查找前缀是否存在

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=10000000;
int idx=0,trie[maxn][26],root,ans,cnt[maxn];
char s[12];
void input(char a[12],int rt)//a字符串是要插入的字符串,rt是root,当前根节点编号 
{
    int len=strlen(a);//取a的长度 
    for(int i=0;i<=len-1;i++)//枚举a中每一个字符 
    {
        int x=a[i]-'a';//当前字母的数字编号 
        if(trie[rt][x]==0)//第二种情况,及树中不存在这个点 (第一种情况不用处理,直接顺着走)
        {
            trie[rt][x]=++idx;//新建一个点(idx是访问顺序) 
        }
        rt=trie[rt][x];    // 将根节点改为当前点,以便下次查找下一个点(顺着走) 
    }
}
int search(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)//如果不存在这个点 
        {
            return 0;//直接返回 0(找不到) 
        }
        rt=trie[rt][x];//更新root 
    }
    return 1;//如果for循环结束了还没有返回,既没有找不到,返回找得到 
}
int main()
{
    root=0;
    while(gets(s) && strcmp(s,"")!=0)//一直输入若干字符串,一直到此行为空  
    {
        input(s,root);
    }
    while(gets(s) && strcmp(s,"")!=0)//一直输入若干字符串,一直到此行为空 
    {
        if(search(s,root)==1)
            cout<<"yes";
        else
            cout<<"no";
    }
} 

 

第二种情况:求以该字符串为前缀的单词的数量(详细方法见注释)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=10000000;
int idx=0,trie[maxn][26],root,ans,cnt[maxn];
char s[12];
void input(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)
        {
            trie[rt][x]=++idx;
        }
        cnt[trie[rt][x]]++;//查询前缀出现次数用的,即包含这个前缀的单词加一 
        rt=trie[rt][x];
    }
}
void search(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)
        {
            ans=0; //前缀出现的次数为零(没有这个前缀) 
            return;
        }
        rt=trie[rt][x];
    }
    ans=cnt[rt];//有这个前缀的单词数 
    return;
}
int main()
{
    root=0;
    while(gets(s) && strcmp(s,"")!=0)
    {
        input(s,root);
    }
    while(gets(s) && strcmp(s,"")!=0)
    {
        search(s,root);
        cout<<ans<<endl;//输出有这个前缀的单词数  
    }
} 

第三种情况:求单词是否出现过(详细方法见注释)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=10000000;
int idx=0,trie[maxn][26],root,ans,cnt[maxn];
char s[12];
int exist[maxn]={0};//标记单词是否存在的数组 
void input(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)
        {
            trie[rt][x]=++idx;
        }
        rt=trie[rt][x];    
    }
    exist[rt]=1;//这个单词存在(要标记在词尾,以保证整个单词都存在) 
}
int search(char a[12],int rt)
{
    int len=strlen(a);
    for(int i=0;i<=len-1;i++)
    {
        int x=a[i]-'a';
        if(trie[rt][x]==0)
        {
            return 0;
        }
        rt=trie[rt][x];
    }
    return exist[rt];//返回是否存在 
}
int main()
{
    root=0;
    while(gets(s) && strcmp(s,"")!=0)
    {
        input(s,root);
    }
    while(gets(s) && strcmp(s,"")!=0)
    {
        if(search(s,root)==1)
            cout<<"yes";
        else
            cout<<"no";
    }
} 

六.模板题

1.hdu 1251 统计难题:http://acm.hdu.edu.cn/showproblem.php?pid=1251

2.洛谷  P2580 于是他错误的点名开始了: https://www.luogu.com.cn/problem/P2580

 

posted @ 2020-03-14 16:49  what_the_heck  阅读(193)  评论(0编辑  收藏  举报