前言

今天具体分析一下trie树,包括:原理分析,应用场合,复杂度分析,与hash的比较,源码展现。大部分内容来自互联网,文中会注明出处。

原理分析

主要是hash树的变种,先看下图:

每一个点存储一个字符,所以trie(字典树)的key不是每个字符串,而是一条链。其原理就是充分利用了公共字符串,这样在查找时,就不需要做重复工作了。并且查找的复杂度可以维持在O(len),len为字符串的长度,原因很简单,我们只需沿着从根到节点的一条路径就可以了。插入也是类似的原理。

建立的过程:

每个节点包括三个信息:26个指针(假设查询26个英文小写字母),每个节点的后继节点可能出现26个字母当中的任何一个,故需26个指针,当然对于不存在的后继结点,设置为NULL;标志位,此标志位主要是为了识别是否为字符串为一个单词;第三个为附加信息,看具体应用场合,可以为字符出现的次数,也可以为前缀的个数,字符串的个数,总之灵活应用就是。

查询的过程:

与建立过程原理雷同,只是没有创建新节点的过程;

删除的过程:

很少见,如果非要删除,则采用递归从下往上挨个delete即可;

应用场合

我直接转载:http://www.cnblogs.com/aiyelinglong/archive/2012/04/09/2439777.html

trie树的应用:

1.有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

2.1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?

3.一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。

4.寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。

后缀树的应用:

1.查找字符串O是否在字符串S中。

方案:用S构造后缀树,按在trie中搜索字串的方法搜索O即可。

原理:若O在S中,则O必然是S的某个后缀的前缀。

例如:leconte,查找O:con是否在S中,则O(con)必然是S(leconte)的前缀。

2.指定字符串T在字符串S中的重复次数。

方案:用S+’$’构造后缀树,搜索T节点下的叶子节点数目即为重复次数

原理:如果T在S中重复了两次,则S应有两个后缀以T为前缀,重复次数自然统计出来了。

3.字符串S中的最长重复子串

方案:原理同2,具体做法是找到最深的非叶子节点。

这个深指从root所经历过的字符个数,最深非叶子节点所经历的字符串起来就是最长重复子串。为什么非要是叶子节点呢?因为既然是要重复的,当然叶子节点个数要>=2

4.两个字符串S1,S2的最长公共子串(而非以前所说的最长公共子序列,因为子序列是不连续的,而子串是连续的。)

方案:将S1#S2$作为字符串压入后缀树,找到最深的非叶子节点,且该节点的叶子节点既有#也有$.

5.最长回文子串

复杂度分析

前文已经提及,建立的时间复杂度为:O(n*len),查询,插入都为O(len)。空间复杂度就比较大了,这也是它的一个缺点,主要是指针得占用空间。

与hash的比较

首先比较创建的复杂度,创建的复杂度,hash为O(n*(len+3))(n指字符串的个数,len指字符串的长度),原理可见我的博文hash 一个海量数据的实现,里面有段代码:

int SDBMHash(char* str)

{

int hash = 0;

while(*str!='\0')

{

hash = *str++ + (hash <<6) + (hash <<16) - hash;

}

return (hash & 0x7FFFFFFF);

}

分析:3具体指int hash = 0; 和return (hash & 0x7FFFFFFF);有人会说,这也算,几乎没影响,但是大家想想,每个字符串多俩次操作,当字符串很大时,就不是俩次的问题了可能是10的几次方了,还有一次是hash表的操作。查询和插入同样的道理,每个字符串多两个操作。所以hash的时间复杂度不如trie的。这还是小case,在很多方面hash没法跟trie比的,比如查找前缀字符串,trie几乎用不到O(len),hash的操作就复杂多了,并且前缀字符串还要额外的hashmap。空间方面,可能hash 节省,但是恰恰就是因为trie牺牲了空间才换如此巨大的时间效果。

源码展现

我自己创建了一个txt文件,里面有很多单词,一行一个,利用trie统计某个单词出现的频数,可在我的资源文件里下到工程文件,里面有一个txt。可以在txt里复制同一个单词多次,然后查询,就可以看到它存在的次数了。

#include<iostream>
#include<cstring>
#include<fstream>
using namespace std; const int n=26;
typedef struct Trie_node
{
int count; // 统计单词前缀出现的次数
struct Trie_node* next[n]; // 指向各个子树的指针
bool exist; // 标记该结点处是否构成单词 }TrieNode , *Trie; TrieNode* createTrieNode()
{
TrieNode* node = (TrieNode *)malloc(sizeof(TrieNode));
node->count = 0;
node->exist = false;
memset(node->next , 0 , sizeof(node->next)); // 初始化为空指针
return node;
} void Trie_insert(Trie root, char* word)
{
Trie node = root;
char *p = word;
int id;
while( *p )
{
id = *p - 'a';
if(node->next[id] == NULL)
{
node->next[id] = createTrieNode();
}
node = node->next[id]; // 每插入一步,相当于有一个新串经过,指针向下移动
++p;
//node->count += 1; // 这行代码用于统计每个单词前缀出现的次数(也包括统计每个单词出现的次数)
}
node->exist = true;// 单词结束的地方标记此处可以构成一个单词
node->count++;
} int Trie_search(Trie root, char* word)
{
Trie node = root;
char *p = word;
int id;
while( *p )
{
id = *p - 'a';
node = node->next[id];
++p;
if(node == NULL)
{
cout<<endl<<word<<"在文件中不存在";
return 0;
}
}
if(node->exist==true)
cout<<endl<<word<<"出现了"<<node->count<<"次";
return node->count; } const int num=5000;
//产生一个txt文件,模拟字符串
void createStrTXT()
{
for(int i=0;i<num;++i)
{
char temp[12]={'\n','\r',rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,'\0'}; char*str=temp;
ofstream ofs("str.txt",ios::app);
ofs<<str;
}
}
void establishTrieTree(Trie root)
{
ifstream ifs("str.txt");
char str[10];
int i=0; while(ifs>>str)
{
Trie_insert(root,str);
cout<<"插入单词:"<<str<<endl;
i++; }
cout<<"总共插入"<<i<<"个单词"; }
int main(void)
{
//初始化root
Trie root=createTrieNode(); //createStrTXT(); establishTrieTree( root); Trie_search(root,"zxuglsdsm"); return 0;
}

测试图:

数据结构 练习21-trie的原理分析和应用的更多相关文章

  1. 数据结构HashMap哈希表原理分析

    先看看定义:“散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度. 哈希 ...

  2. JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balaba ...

  3. (6)Java数据结构-- 转:JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析  http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balab ...

  4. Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题

    Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...

  5. JAVA经常使用数据结构及原理分析

    前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源代码,balabala讲了一堆,如今总结一下. java.util包中三个重要的接口及特点:List(列表).Set(保证集合中元素 ...

  6. HashMap 与 ConcrrentHashMap 使用以及源码原理分析

    前奏一:HashMap面试中常见问题汇总 HashMap的工作原理是近年来常见的Java面试题,几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和Has ...

  7. 基于Lucene查询原理分析Elasticsearch的性能

    前言 Elasticsearch是一个很火的分布式搜索系统,提供了非常强大而且易用的查询和分析能力,包括全文索引.模糊查询.多条件组合查询.地理位置查询等等,而且具有一定的分析聚合能力.因为其查询场景 ...

  8. [转]Handler MessageQueue Looper消息循环原理分析

    Handler MessageQueue Looper消息循环原理分析   Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...

  9. Android ListView实现不同item的方法和原理分析

    ListView实现不同item的方法和原理分析 一问题抛出Listview是android里面的重要组件,用来显示一个竖向列表,这个没有什么问题:但是有个时候列表里面的item不是一样的,如下图,列 ...

随机推荐

  1. 【BZOJ】1621: [Usaco2008 Open]Roads Around The Farm分岔路口(dfs)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1621 这题用笔推一下就懂了的.... 当2|(n-k)时,才能分,否则不能分. 那么dfs即可.. ...

  2. 【BZOJ】1641: [Usaco2007 Nov]Cow Hurdles 奶牛跨栏(floyd)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1641 这种水题无意义... #include <cstdio> #include < ...

  3. 将spark默认日志log4j替换为logback

    1.将jars文件夹下apache-log4j-extras-1.2.17.jar,commons-logging-1.1.3.jar, log4j-1.2.17.jar, slf4j-log4j12 ...

  4. MathType二次偏导怎么表示

    求导以及求偏导运算在数学中是很重要的一个部分,尤其是在高等数学中,基本都由函数的导数与偏导组成,很多公式定理也是关于这方面的,如果少了这一部分,数学将会黯然失色.因此在文档中涉及到这些内容时,必然会少 ...

  5. iOS开发之--如何修改TabBarItem的title的字体和颜色/BarButtonItem的title的字体大小和颜色/添加背景图片,并添加点击方法

    在进行项目的过程中,我们往往会遇到各种各样的自定义颜色和字体,下面提供一种修改系统自带的TabBarItem的字体和颜色的方法,希望能帮到大家: [[UITabBarItem appearance] ...

  6. 中文转换成Unicode编码 和 Unicode编码转换为中文

    前几天,遇到一个问题,就是在浏览器地址栏传递中文时,出现乱码,考虑了一下,解决方式有很多,我还是采用了转换编码的方式,将中文转换为Unicode编码,然后再解码成中文,以下是实现的过程,非常简单! p ...

  7. ionic跳转(二)

    1)网上说要想在js里跳转用,$state.go()方法,但找了大半天都没找到在ionic使用$state的方法 2)要想用js跳转,直接用原生js跳转也是可以的 location.href='#ho ...

  8. Popwindow系列

    一系列干货等你来拿 关于我:http://www.cnblogs.com/dubo-/ 项目中也有很多实用的事例,等待录入中...

  9. 模拟window桌面实现

    正在开发中的游戏有个全屏功能--可以在window桌面背景上运行,就像一些视频播放器在桌面背景上播放一样的,花了个上午整了个Demo放出来留个纪念. 实现功能:显示图标,双击图标执行相应的程序,右击图 ...

  10. oracle 存储过程返回结果集

    好久没上来了, 难道今天工作时间稍有空闲, 研究了一下oracle存储过程返回结果集. 配合oracle临时表, 使用存储过程来返回结果集的数据读取方式可以解决海量数据表与其他表的连接问题. 在存储过 ...