数据结构 练习21-trie的原理分析和应用

前言

今天具体分析一下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)  
{  //初始化rootTrie root=createTrieNode();//createStrTXT();establishTrieTree( root);Trie_search(root,"zxuglsdsm");return 0;  
}  


测试图:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在辞职后的旅途中:我写了个App 创立了一家公司

http://www.cocoachina.com/programmer/20150206/11119.html 英文原文&#xff1a;How I built a startup while traveling to 20 countries 一年前&#xff0c;我离开了旧金山&#xff0c;变卖或者送掉了一切我所拥有的东西&#xff0c;然后买了一只 40 升的登山包。 我旅行到…

Be My Eyes app:我是你的眼

http://www.cocoachina.com/industry/20150122/10979.html Be My Eyes是丹麦软件工作室Robocat为一家同名非营利性企业推出的一款应用&#xff0c;主要通过视频聊天的方式将志愿者和视力受损的患者联系起来&#xff0c;从而实现远程协助的功能。 Be My Eyes的核心概念非常简单-…

nRF905

nRF905[1]无线芯片是有挪威NORDIC公司出品的低于1GHz无线数传芯片&#xff0c;主要工作于433MHz、868MHz和915MHz的ISM频段。芯片内置频率合成器、功率放大器、晶体振荡器和调制器等功能模块&#xff0c;输出功率和通信频道可通过程序进行配置。非常适合于低功耗、低成本的系统…

Firefox for iOS现身Github 使用Swift编写

http://www.cocoachina.com/industry/20141208/10545.html 自从Mozilla新CEO走马上任以来&#xff0c;该公司对于发展路线显然与以往有所不同&#xff0c;对该公司最重要的产品Firefox浏览器来说&#xff0c;也有了很多大的改变&#xff0c;包括前几天Mozilla宣布&#xff0c;它…

UVA 213 Message Decoding

题目链接&#xff1a;https://vjudge.net/problem/UVA-213 题目翻译摘自《算法禁赛入门经典》 题目大意 考虑下面的 01 串序列&#xff1a;  0, 00, 01, 10, 000, 001, 010, 011, 100, 101, 110, 0000, 0001, …, 1101, 1110, 00000, …  首先是长度为 1 的串&#xff0c;然…

分组取最新记录的SQL

常遇到这样的情况&#xff0c;要取得所有客户的最新交易记录&#xff0c;读取网站所有浏览者最后一次访问时间。一个客户只读取最新的一次记录&#xff0c;相同&#xff0c;大部分的人首先想 到的就是排除所有记录&#xff0c;相同的只取一条。用distint,但是distint只能取到一…

利用CVE-2019-1040 - 结合RCE和Domain Admin的中继漏洞

0x00 前言 在本周之前&#xff0c;微软发布了针对CVE-2019-1040的补丁&#xff0c;这是一个允许绕过NTLM身份验证中继攻击的漏洞。这个漏洞是由Marina Simakov和Yaron Zinar&#xff08;以及微软咨询公司的其他几位成员&#xff09;发现的&#xff0c;他们在这里发表了一篇关于…

[转]DEV界面

DevExpress控件使用经验总结 DevExpress是一个比较有名的界面控件套件&#xff0c;提供了一系列的界面控件套件的DotNet界面控件。本文主要介绍我在使用DevExpress控件过程中&#xff0c;遇到或者发现的一些问题解决方案&#xff0c;或者也可以所示一些小的经验总结。总体来讲&…

postgresql安装配置

postgresql安装配置 一,什么是postgresql PostgreSQL是以加州大学伯克利分校计算机系开发的 POSTGRES 版本 4.2 为基础的对象关系型数据库管理系统&#xff08;ORDBMS&#xff09;,简称pgsql,它支持大部分 SQL 标准并且提供了许多其他现代特性&#xff1a;复杂查询 外键 触发器…

[转]Messenger:使用消息的跨进程通信

本文转自&#xff1a;http://xwangly.iteye.com/blog/1109424 Messenger:信使 官方文档解释&#xff1a;它引用了一个Handler对象&#xff0c;以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Mess…

python 编程模型

数据模型&#xff08;译&#xff09; image.png1 对象&#xff08;object&#xff09;、类型&#xff08;type&#xff09;和值&#xff08;value&#xff09; python中所有的数据都是通过对象&#xff08;object&#xff09;或者对象之间的关系来表示 每个对象&#xff08;obj…

R中统计假设检验总结(一)

先PS一个&#xff1a;考虑到这次的题目本身的特点 尝试下把说明性内容都直接作为备注写在语句中 另外用于说明的部分例子参考了我的教授Guy Yollin在Financial Data Analysis and Modeling with R这门课课件上的例子 部分参考了相关package的帮助文档中的例子 下面正题- 戌 >…

改造MUC实现Openfire群

我的Openfire群实现思路&#xff1a; 1、群和群成员&#xff0c;要保存到表中。 2、拉取群列表和群成员列表&#xff0c;均从DB中查询返回。 3、抛弃老外的“进房间&#xff0c;要发Presence ”。只要此人一上线&#xff0c;就模似一个Presence进行joinRoom&#xff0c;进入他的…

如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信

http://blog.csdn.net/whuancai/article/details/11994341 如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信 原文出自&#xff1a;http://blog.csdn.net/monkey_d_meng/article/details/5894910 尊重作者&#xff1a;MONKEY_D_MENG 最近一段时间&#xff0c;由…

14 Scroll 滚动搜索

Scroll的用法&#xff1a;第一次搜的时候&#xff0c;要指定 快照保留时间1min&#xff0c;分页的大小&#xff1a;2条/页&#xff1b;对于第一次搜索&#xff0c;ES会返回一个这个scroll的id&#xff1b;下次再搜的时候&#xff0c;就带着这个scrollid去搜就行了&#xff0c;不…

VS2008中使用JSONCPP方法小结

http://sourceforge.net/projects/jsoncpp/?sourcetyp_redirect C要使用JSON来解析数据&#xff0c;一般采用jsoncpp. 下载jsoncpp后&#xff0c;按ReadMe文档的说法是要先安装的&#xff0c;但是安装比较麻烦。然而事实上&#xff0c;我们并不需要安装&#xff0c;就可以直接…

如何在Windows下编译OpenSSL

OpenSSL是一个开源的第三方库&#xff0c;它实现了SSL&#xff08;Secure SocketLayer&#xff09;和TLS&#xff08;Transport Layer Security&#xff09;协议&#xff0c;被广泛企业应用所采用。对于一般的开发人员而言&#xff0c;在 http://slproweb.com/products/Win32Op…

《团队名称》第八次团队作业:Alpha冲刺day5

项目内容这个作业属于哪个课程2016计算机科学与工程学院软件工程(西北师范大学)这个作业的要求在哪里实验十二 团队作业8—软件测试与ALPHA冲刺团队名称快活帮作业学习目标 &#xff08;1&#xff09;掌握软件测试基础技术。 &#xff08;2&#xff09;学习迭代式增量软件开发过…

(转)C# 把我所积累的类库全部分享给博友(附件已经上传)

http://files.cnblogs.com/LsGW/Common.zip转载于:https://www.cnblogs.com/meetrice/archive/2012/01/02/2310428.html

前端的小图标获取

搜索iconfont&#xff0c;里面有很多图标&#xff0c;鼠标移到想要的图标上&#xff0c;然后点击一个类似购物车的图标&#xff0c;然后添加到项目&#xff0c;下载到本地&#xff0c;有一个使用指南的html&#xff0c;然后参照上面的改就好。 把下载好的.css和.eot文件拖到css…