字典树讲解

来源:互联网 发布:ipad清理空间软件 编辑:程序博客网 时间:2024/09/21 11:15

Trie树就是字典树,核心思想就是用空间来换时间。


举个栗子:(转)


现在有 100000 个长度不超过 10 的单词。对于每一个单词,我们要判断他有没有出现过,如果出现了,第一次出现在第几个位置。
这题当然可以用hash来解决,但是这里要介绍的是 trie 树。在某些方面它的用途更大。比如说对于某一个单词,要询问它的前缀是否出现过。这样 hash 就不好解决了,而用 trie 还是很简单。
现在回到例子中,如果我们用最原始的方法,对于每一个单词,我们都要去查找它前面的单词中是否有它。那么这个算法的复杂度就是O(n^2)。显然对于100000的范围难以接受。现在我们换个思路想。假设我要查询的单词是abcd,那么在他前面的单词中,以b,c,d,f之类开头的我显然不必考虑。而只要找以a开头的中是否存在abcd就可以了。同样的,在以a开头中的单词中,我们只要考虑以b作为第二个字母的……这样一个树的模型就渐渐清晰了……
假设有b,abc,abd,bcd,abcd,efg,hii 这6个单词,我们构建的树就是这样的。



对于每一个节点,从根遍历到他的过程就是一个单词,如果这个节点被标记为红色,就表示这个单词存在,否则不存在。
那么,对于一个单词,我只要顺着他从根走到对应的节点,再看这个节点是否被标记为红色就可以知道它是否出现过了。把这个节点标记为红色,就相当于插入了这个单词。
这样一来我们询问和插入可以一起完成,所用时间仅仅为单词长度,在这一个样例,便是10。
我们可以看到,trie树每一层的节点数是26^i 级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。

模版一:(统计是否存在相同前缀)


#include <cstdio>#include <cstring>#include <malloc.h>#include <iostream>using namespace std;#define MAXN 10typedef struct Trie{    int v;//根据需要变化    Trie *next[MAXN];    //next是表示每层有多少种类的数,如果只是小写字母,则26即可,    //若改为大小写字母,则是52,若再加上数字,则是62了}Trie;Trie* root;void createTrie(char *str){    int len = strlen(str);    Trie *p = root, *q;    for(int i = 0; i < len; i++)    {        int id = str[i]-'0';        if(p->next[id] == NULL)        {            q = (Trie *)malloc(sizeof(Trie));            q->v = 1;//初始v==1            for(int j = 0; j < MAXN; j++)                q->next[j] = NULL;            p->next[id] = q;            p = p->next[id];        }        else        {        //    p->next[id]->v++;            p = p->next[id];        }    }     p->v = -1;//若为结尾,则将v改成-1表示}int findTrie(char *str){    int len = strlen(str);    Trie *p = root;    for(int i = 0; i < len; i++)    {        int id = str[i]-'0';        p = p->next[id];        if(p == NULL) //若为空集,表示不存以此为前缀的串            return 0;        if(p->v == -1)   //字符集中已有串是此串的前缀            return -1;    }    //return p->v;    return -1;   //此串是字符集中某串的前缀}int dealTrie(Trie* T){    //动态字典树,有时会超内存,这是就要记得释放空间了    if(T==NULL)        return 0;    for(int i = 0; i < MAXN; i++)    {        if(T->next[i]!=NULL)            dealTrie(T->next[i]);    }    free(T);    return 0;}int main(){    int t, n;    char str[15];    scanf("%d",&t);    while(t--)    {        int flag = 0;        root = (Trie *)malloc(sizeof(Trie));        for(int i = 0; i < MAXN; i++)            root->next[i] = NULL;        scanf("%d",&n);        for(int i = 0; i < n; i++)        {            scanf("%s",str);            if(findTrie(str) == -1)            {                flag = 1;                continue;            }            createTrie(str);        }        if(flag)            printf("NO\n");        else            printf("YES\n");        dealTrie(root);    }    return 0;}



模板二:(统计前缀相同数量)

#include <cstdio>#include <cstring>#include <malloc.h>#include <iostream>using namespace std;#define MAXN 26typedef struct Trie{    int v;//根据需要变化    Trie *next[MAXN];    //next是表示每层有多少种类的数,如果只是小写字母,则26即可,    //若改为大小写字母,则是52,若再加上数字,则是62了}Trie;Trie *root;void createTrie(char *str){    int len = strlen(str);    Trie *p = root, *q;    for(int i = 0; i < len; i++)    {        int id = str[i]-'a';        if(p->next[id] == NULL)        {            q = (Trie *)malloc(sizeof(Trie));            q->v = 1;//初始v==1            for(int j = 0; j < MAXN; j++)                q->next[j] = NULL;            p->next[id] = q;            p = p->next[id];        }        else        {            p->next[id]->v++;            p = p->next[id];        }    }    // p->v = -1;//若为结尾,则将v改成-1表示}int findTrie(char *str){    int len = strlen(str);    Trie *p = root;    for(int i = 0; i < len; i++)    {        int id = str[i]-'a';        p = p->next[id];        if(p == NULL) //若为空集,表示不存以此为前缀的串            return 0;      //  if(p->v == -1)   //字符集中已有串是此串的前缀      //      return -1;    }    return p->v;    //return -1;   //此串是字符集中某串的前缀}int dealTrie(Trie* T){    //动态字典树,有时会超内存,这时就要记得释放空间了    if(T==NULL)        return 0;    for(int i = 0; i < MAXN; i++)    {        if(T->next[i]!=NULL)            dealTrie(T->next[i]);    }    free(T);    return 0;}int main(){    char str[15];    root = (Trie *)malloc(sizeof(Trie));    for(int i = 0; i < MAXN; i++)        root->next[i] = NULL;    while(gets(str) && str[0]!='\0')        createTrie(str);    memset(str, 0, sizeof(str));    while(scanf("%s", str) != EOF)    {        int ans = findTrie(str);        printf("%d\n", ans);    }    return 0;}


1 0