1. 什么是trie树

1.Trie树 (特例结构树)

      Trie树,又称单词查找树、字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
     Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
     Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.

2.  三个基本特性:  

1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。  
2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 
3)每个节点的所有子节点包含的字符都不相同。

3 .例子

和二叉查找树不同,在trie树中,每个结点上并非存储一个元素。
       trie树把要查找的关键词看作一个字符序列。并根据构成关键词字符的先后顺序构造用于检索的树结构。
       在trie树上进行检索类似于查阅英语词典。

一棵m度的trie树或者为空,或者由m棵m度的trie树构成。

例如,电子英文词典,为了方便用户快速检索英语单词,可以建立一棵trie树。例如词典由下面的单词成:a、b、c、aa、ab、ac、ba、ca、aba、abc、baa、bab、bac、cab、abba、baba、caba、abaca、caaba

再举一个例子。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:

可以看出:

  • 每条边对应一个字母。
  • 每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
  • 单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀"a",所以他们共享从根节点到节点"a"的边。

查询操纵非常简单。比如要查找int,顺着路径i -> in -> int就找到了。

2. trie树的实现

1.插入过程

对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支向下走,直到单词遍历完,将最后的节点标记为红色,表示该单词已插入trie树。

2. 查找过程

其方法为:

(1) 从根结点开始一次搜索;

(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;

(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……
(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。其他操作类似处理.

即从根开始按照单词的字母顺序向下遍历trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未标记为红色,则表示该单词不存在,若最后的节点标记为红色,表示该单词存在。如下图中:trie树中存在的就是abc、d、da、dda四个单词。在实际的问题中可以将标记颜色的标志位改为数量count等其他符合题目要求的变量。

代码:
[cpp] view plain copy  print?
  1. // stdafx.h : include file for standard system include files,
  2. // or project specific include files that are used frequently, but
  3. // are changed infrequently
  4. //
  5. #pragma once
  6. #include <stdio.h>
  7. #include "stdlib.h"
  8. #include <iostream>
  9. #include <string.h>
  10. using namespace std;
  11. //宏定义
  12. #define TRUE   1
  13. #define FALSE   0
  14. #define NULL 0
  15. #define OK    1
  16. #define ERROR   0
  17. #define INFEASIBLE -1
  18. #define OVERFLOW -2
  19. const int num_chars = 26;
  20. class Trie {
  21. public:
  22. Trie();
  23. Trie(Trie& tr);
  24. virtual ~Trie();
  25. int trie_search(const char* word, char* entry ) const;
  26. int insert(const char* word, const char* entry);
  27. int remove(const char* word, char* entry);
  28. protected:
  29. struct Trie_node{
  30. char* data; //若不为空,表示从root到此结点构成一个单词
  31. Trie_node* branch[num_chars]; //分支
  32. Trie_node(); //构造函数
  33. };
  34. Trie_node* root; //根结点(指针)
  35. };
[cpp] view plain copy  print?
  1. // Test.cpp : Defines the entry point for the console application.
  2. //
  3. #include "stdafx.h"
  4. Trie::Trie_node::Trie_node() {
  5. data = NULL;
  6. for (int i=0; i<num_chars; ++i)
  7. branch[i] = NULL;
  8. }
  9. Trie::Trie():root(NULL) {}
  10. Trie::~Trie(){}
  11. int Trie::trie_search(const char* word, char* entry ) const {
  12. int position = 0;  //层数
  13. char char_code;
  14. Trie_node *location = root;  //从根结点开始
  15. while( location!=NULL && *word!=0 ) {
  16. if (*word >= 'A' && *word <= 'Z')
  17. char_code = *word-'A';
  18. else if (*word>='a' && *word<='z')
  19. char_code = *word-'a';
  20. else return 0;// 不合法的单词
  21. //转入相应分支指针
  22. location = location->branch[char_code];
  23. position++;
  24. word++;
  25. }
  26. //找到,获取数据,成功返回
  27. if ( location != NULL && location->data != NULL ) {
  28. strcpy(entry,location->data);
  29. return 1;
  30. }
  31. else  return 0;// 不合法的单词
  32. }
  33. int Trie::insert(const char* word, const char* entry) {
  34. int result = 1, position = 0;
  35. if ( root == NULL ) root = new Trie_node;   //初始插入,根结点为空
  36. char char_code;
  37. Trie_node *location = root;   //从根结点开始
  38. while( location!=NULL && *word!=0 ) {
  39. if (*word>='A' && *word<='Z') char_code = *word-'A';
  40. else if (*word>='a' && *word<='z') char_code = *word-'a';
  41. else return 0;// 不合法的单词
  42. //不存在此分支
  43. if( location->branch[char_code] == NULL )
  44. location->branch[char_code] = new Trie_node;    //创建空分支
  45. //转入分支
  46. location = location->branch[char_code];
  47. position++;word++;   }
  48. if (location->data != NULL) result = 0;//欲插入的单词已经存在
  49. else {    //插入数据
  50. location->data = new char[strlen(entry)+1];     //分配内存
  51. strcpy(location->data, entry);    //给data赋值表明单词存在
  52. }
  53. return result;
  54. }
  55. int main(){
  56. Trie t;
  57. char entry[100];
  58. t.insert("a", "DET");
  59. t.insert("abacus","NOUN");
  60. t.insert("abalone","NOUN");
  61. t.insert("abandon","VERB");
  62. t.insert("abandoned","ADJ");
  63. t.insert("abashed","ADJ");
  64. t.insert("abate","VERB");
  65. t.insert("this", "PRON");
  66. if (t.trie_search("this", entry))
  67. cout<<"'this' was found. pos: "<<entry<<endl;
  68. if (t.trie_search("abate", entry))
  69. cout<<"'abate' is found. pos: "<<entry<<endl;
  70. if (t.trie_search("baby", entry))
  71. cout<<"'baby' is found. pos: "<<entry<<endl;
  72. else
  73. cout<<"'baby' does not exist at all!"<<endl;
  74. }

3. 查找分析

在trie树中查找一个关键字的时间和树中包含的结点数无关,而取决于组成关键字的字符数。而二叉查找树的查找时间和树中的结点数有关O(log2n)。
如果要查找的关键字可以分解成字符序列且不是很长,利用trie树查找速度优于二叉查找树。如:
       若关键字长度最大是5,则利用trie树,利用5次比较可以从26^5=11881376个可能的关键字中检索出指定的关键字。而利用二叉查找树至少要进行次比较。

3. trie树的应用:

1. 字符串检索,词频统计,搜索引擎的热门查询

事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。

举例:

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

2)给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

3)给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。

4)1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串

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

2. 字符串最长公共前缀

Trie树利用多个字符串的公共前缀来节省存储空间,反之,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。举例:

1) 给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少.  解决方案:

首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线  (Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。

而最近公共祖先问题同样是一个经典问题,可以用下面几种方法:

1. 利用并查集(Disjoint Set),可以采用采用经典的Tarjan 算法;

2. 求出字母树的欧拉序列(Euler Sequence )后,就可以转为经典的最小值查询(Range Minimum Query,简称RMQ)问题了;

3.  排序

Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。

举例: 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。

4 作为其他数据结构和算法的辅助结构

如后缀树,AC自动机等。

Trie树:应用于统计和排序相关推荐

  1. 算法 | 动画+解析,轻松理解「Trie树」

    Trie这个名字取自"retrieval",检索,因为Trie可以只用一个前缀便可以在一部字典中找到想要的单词. 虽然发音与「Tree」一致,但为了将这种 字典树 与 普通二叉树 ...

  2. SZUACM集训字符串基础总结: 字符串最小表示 ,KMP, EXKMP, Manracher, Trie树,字符串的hash; 附带一写常见的运用技巧,邝斌大佬的板子和例题[持续更新]

    第一部分 字符串的匹配<-------->KMP 模式匹配:子串的定位运算称为串的模式匹配或串匹配. 假设有两个串S,T,设S为主串,也称正文串,T为子串,也称为模式,在主串S中查找与模式 ...

  3. 海量数据处理-Trie树

    http://blog.csdn.net/beiyeqingteng/article/details/6981263 http://blog.csdn.net/zmazon/article/detai ...

  4. 浅谈 trie树 及其实现

    定义:又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构, 如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 核心思想:是空间换时间.利用字符串的公共前缀来降低查询时间的开 ...

  5. Trie树(字典树)详细知识点及其应用

    Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,PATRICIA tree,以及bitwise版本的crit-bit tree.当然很多名字的意义其实有交 ...

  6. trie树和后缀树的应用

    Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地 ...

  7. 看动画轻松理解「Trie树」

    作者 | 程序员小吴 责编 | 胡巍巍 扎心!"我学了半年 Python,还是找不到工作" https://edu.csdn.net/topic/python115?utm_sou ...

  8. 前缀树(字典树,单词查找树,Trie树)

    参考网址:https://blog.csdn.net/u013949069/article/details/78056102?utm_source=copy 概述 前缀树又名字典树,单词查找树,Tri ...

  9. 自然语言--Trie树详解及其应用

    链接:http://blog.csdn.net/hackbuteer1/article/details/7964147 参考链接:https://segmentfault.com/a/11900000 ...

最新文章

  1. Android开发过程中内存泄露检测
  2. 【知识星球】softmax损失相关的小问题
  3. ios 构建版本一直在处理中_iOS构建版本不显示
  4. Android2D理解坑
  5. Linux学习总结(2)——linux常用命令大全
  6. git---Git中.gitignore配置
  7. HTTP和RPC的区别?
  8. 解决Windows上编译PHP7.1拓展的错误
  9. 小游戏开发引擎CocosCreator
  10. wps的range对象
  11. fastadmin 表格文字实现换行
  12. 手机QQ邮箱登录不上的解决办法
  13. 当3D应用遇上云流送技术使用更方便
  14. 将selinux=enforing设置好过后,重启Linux管理员账号不能登陆
  15. android电视无线同屏,手机连接电视同屏操作方法详解
  16. 【飞行器】模拟四旋翼飞行器飞行含Matlab源码
  17. 傅里叶变换 ~ 离散傅里叶变换(DFT)
  18. SEO优化考核的七大指标
  19. 钉钉windows端多开软件_Windows7系统便签怎么找?适合Windows系统的便签
  20. EDM营销中老外眼中的常见垃圾邮件关键词

热门文章

  1. readSerializableObj
  2. 30天敏捷结果(2):用三个故事驱动你的一周
  3. CodeForces - 1307C Cow and Message(思维)
  4. CodeForces - 932D Tree(树上倍增,好题)
  5. CodeForces - 1036B Diagonal Walking v.2(思维)
  6. python import xlrd 报错_python读取excel(xlrd)
  7. debian重启ssh服务_Jenkins远程部署Linux服务器
  8. 对于我的博客的相关说明
  9. gh0st源码分析:屏幕监控
  10. SSDT Hook的妙用-对抗ring0 inline hook