前言

今天具体分析一下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. HTML邮件注意事项(转)

    1.全局规则之一,不要写<style>标签.不要写class,所有CSS都用style属性,什么元素需要什么样式就用style写内联的CSS. 2.全局规则之二,少用图片,邮箱不会过滤你的 ...

  2. LR的响应时间与使用IE所感受时间不一致的讨论(摘抄补充)

    http://www.51testing.com/html/33/564333-865629.html 在做性能测试时,有时碰到这样一种情况,使用性能工具LR测试出来的响应时间比实际使用IE感受到的时 ...

  3. python入门(八):连接mysql和STMP

    Python3 MySQL 数据库连接,使用 PyMySQL 连接数据库,并实现简单的增删改查.  mysql连接步骤 1.打开数据库连接 2.使用cursor()方法获取操作游标 3.执行sql和异 ...

  4. IDEA破解后无法启动

    在网上找了破解IDEA的方法 原文:https://blog.csdn.net/qq_38637558/article/details/78914772 ①到这个地方下载 IntelliJ IDEA ...

  5. mysql用sql创建表完整实例

    create table user_login_latest( id int(11) unsigned NOT NULL AUTO_INCREMENT, user_id int(11) not nul ...

  6. XStream别名;元素转属性;去除集合属性(剥皮);忽略不需要元素

    city package xstream; public class City { private String name; private String description; public St ...

  7. 170314、工具:apache httpClient多线程并发情况下安全实用及工具类分享

    简单用法介绍:介绍来源网络 建立连接:在HttpClient中使用多线程的一个主要原因是可以一次执行多个方法.在执行期间,每一个方法都使用一个HttpConnection实例.由于在同一时间多个连接只 ...

  8. 160613、MyBatis insert操作返回主键

    在使用MyBatis做持久层时,insert语句默认是不返回记录的主键值,而是返回插入的记录条数:如果业务层需要得到记录的主键时,可以通过配置的方式来完成这个功能,针对Sequence主键而言,在执行 ...

  9. 160411、实时监控mysql数据库变化

    对于二次开发来说,很大一部分就找找文件和找数据库的变化情况 对于数据库变化.还没有发现比较好用的监控数据库变化监控软件. 今天,我就给大家介绍一个如何使用mysql自带的功能监控数据库变化 1.打开数 ...

  10. Zabbix分布式监控

    上一篇:Zabbix的API的使用 zabbix分布式监控 新建一台主机 安装zabbix proxy和数据库 yum -y install mariadb-server zabbix-proxy-m ...