trie树--详解

文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 转载请注明,谢谢合作。

关键词:trie trie树 数据结构

 

    前几天学习了并查集和trie树,这里总结一下trie。
    本文讨论一棵最简单的trie树,基于英文26个字母组成的字符串,讨论插入字符串、判断前缀是否存在、查找字符串等基本操作;至于trie树的删除单个节点实在是少见,故在此不做详解。

l        Trie原理

Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

 

l        Trie性质

好多人说trie的根节点不包含任何字符信息,我所习惯的trie根节点却是包含信息的,而且认为这样也方便,下面说一下它的性质 (基于本文所讨论的简单trie树)

1.    字符的种数决定每个节点的出度,即branch数组(空间换时间思想)

2.    branch数组的下标代表字符相对于a的相对位置

3.    采用标记的方法确定是否为字符串。

4.    插入、查找的复杂度均为O(len),len为字符串长度

l        Trie的示意图

 

如图所示,该trie树存有abc、d、da、dda四个字符串,如果是字符串会在节点的尾部进行标记。没有后续字符的branch分支指向NULL

trieTree.JPG



l       
Trie
Trie的优点举例

已知n个由小写字母构成的平均长度为10的单词,判断其中是否存在某个串为另一个串的前缀子串。下面对比3种方法:

1.    最容易想到的:即从字符串集中从头往后搜,看每个字符串是否为字符串集中某个字符串的前缀,复杂度为O(n^2)。

2.    使用hash:我们用hash存下所有字符串的所有的前缀子串。建立存有子串hash的复杂度为O(n*len)。查询的复杂度为O(n)* O(1)= O(n)。

3.    使用trie:因为当查询如字符串abc是否为某个字符串的前缀时,显然以b,c,d....等不是以a开头的字符串就不用查找了。所以建立trie的复杂度为O(n*len),而建立+查询在trie中是可以同时执行的,建立的过程也就可以成为查询的过程,hash就不能实现这个功能。所以总的复杂度为O(n*len),实际查询的复杂度只是O(len)。


解释一下hash为什么不能将建立与查询同时执行,例如有串:911,911456输入,如果要同时执行建立与查询,过程就是查询911,没有,然后存入9、91、911,查询911456,没有然后存入9114、91145、911456,而程序没有记忆功能,并不知道911在输入数据中出现过。所以用hash必须先存入所有子串,然后for循环查询。

而trie树便可以,存入911后,已经记录911为出现的字符串,在存入911456的过程中就能发现而输出答案;倒过来亦可以,先存入911456,在存入911时,当指针指向最后一个1时,程序会发现这个1已经存在,说明911必定是某个字符串的前缀,该思想是我在做pku上的3630中发现的,详见本文配套的“入门练习”。

l        Trie的简单实现(插入、查询)

 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1None.gif
 2None.gif#include <iostream>
 3None.gifusing namespace std;
 4None.gif
 5None.gifconst int branchNum = 26//声明常量 
 6None.gifint i;
 7None.gif
 8None.gifstruct Trie_node
 9ExpandedBlockStart.gifContractedBlock.gif {
10InBlock.gif    bool isStr; //记录此处是否构成一个串。
11InBlock.gif    Trie_node *next[branchNum];//指向各个子树的指针,下标0-25代表26字符
12InBlock.gif    Trie_node():isStr(false)
13ExpandedSubBlockStart.gifContractedSubBlock.gif    {
14InBlock.gif        memset(next,NULL,sizeof(next));
15ExpandedSubBlockEnd.gif    }

16ExpandedBlockEnd.gif}
;
17None.gif
18None.gifclass Trie
19ExpandedBlockStart.gifContractedBlock.gif{
20InBlock.gifpublic:
21InBlock.gif    Trie();
22InBlock.gif    void insert(const char* word);
23InBlock.gif    bool search(char* word); 
24InBlock.gif    void deleteTrie(Trie_node *root);
25InBlock.gifprivate:
26InBlock.gif    Trie_node* root;
27ExpandedBlockEnd.gif}
;
28None.gif
29None.gifTrie::Trie()
30ExpandedBlockStart.gifContractedBlock.gif{
31InBlock.gif    root = new Trie_node();
32ExpandedBlockEnd.gif}

33None.gif
34None.gifvoid Trie::insert(const char* word)
35ExpandedBlockStart.gifContractedBlock.gif{
36InBlock.gif    Trie_node *location = root;
37InBlock.gif    while(*word)
38ExpandedSubBlockStart.gifContractedSubBlock.gif    {
39InBlock.gif        if(location->next[*word-'a'== NULL)//不存在则建立
40ExpandedSubBlockStart.gifContractedSubBlock.gif        {
41InBlock.gif            Trie_node *tmp = new Trie_node();
42InBlock.gif            location->next[*word-'a'= tmp;
43ExpandedSubBlockEnd.gif        }
    
44InBlock.gif        location = location->next[*word-'a']; //每插入一步,相当于有一个新串经过,指针要向下移动
45InBlock.gif        word++;
46ExpandedSubBlockEnd.gif    }

47InBlock.gif    location->isStr = true//到达尾部,标记一个串
48ExpandedBlockEnd.gif}

49None.gif
50None.gifbool Trie::search(char *word)
51ExpandedBlockStart.gifContractedBlock.gif{
52InBlock.gif    Trie_node *location = root;
53InBlock.gif    while(*word && location)
54ExpandedSubBlockStart.gifContractedSubBlock.gif    {
55InBlock.gif        location = location->next[*word-'a'];
56InBlock.gif        word++;
57ExpandedSubBlockEnd.gif    }

58InBlock.gif    return(location!=NULL && location->isStr);
59ExpandedBlockEnd.gif}

60None.gif
61None.gifvoid Trie::deleteTrie(Trie_node *root)
62ExpandedBlockStart.gifContractedBlock.gif{
63InBlock.gif    for(i = 0; i < branchNum; i++)
64ExpandedSubBlockStart.gifContractedSubBlock.gif    {
65InBlock.gif        if(root->next[i] != NULL)
66ExpandedSubBlockStart.gifContractedSubBlock.gif        {
67InBlock.gif            deleteTrie(root->next[i]);
68ExpandedSubBlockEnd.gif        }

69ExpandedSubBlockEnd.gif    }

70InBlock.gif    delete root;
71ExpandedBlockEnd.gif}

72None.gif
73None.gifvoid main() //简单测试
74ExpandedBlockStart.gifContractedBlock.gifdot.gif{
75InBlock.gif    Trie t;
76InBlock.gif    t.insert("a");         
77InBlock.gif    t.insert("abandon");
78InBlock.gif    char * c = "abandoned";
79InBlock.gif    t.insert(c);
80InBlock.gif    t.insert("abashed");
81InBlock.gif    if(t.search("abashed"))
82InBlock.gif        printf("true\n");
83ExpandedBlockEnd.gif}

转载于:https://www.cnblogs.com/spirals/archive/2010/09/16/1828448.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值