Trie 树, 又称字典树,单词查找树。它来源于retrieval(检索)中取中间四个字符构成(读音同try)。用于存储大量的字符串以便支持快速模式匹配。主要应用在信息检索领域。

Trie 有三种结构: 标准trie (standard trie)、压缩trie、后缀trie(suffix trie) 。 最后一种将在《字符串处理4:后缀树》中详细讲,这里只将前两种。

1. 标准Trie (standard trie)

标准 Trie树的结构 : 所有含有公共前缀的字符串将挂在树中同一个结点下。实际上trie简明的存储了存在于串集合中的所有公共前缀。 假如有这样一个字符串集合X{bear,bell,bid,bull,buy,sell,stock,stop}。它的标准Trie树如下图:

上图(蓝色圆形结点为内部结点,红色方形结点为外部结点),我们可以很清楚的看到字符串集合X构造的Trie树结构。其中从根结点到红色方框叶子节点所经历的所有字符组成的串就是字符串集合X中的一个串。

注意这里有一个问题: 如果X集合中有一个串是另一个串的前缀呢? 比如,X集合中加入串bi。那么上图的Trie树在绿色箭头所指的内部结点i 就应该也标记成红色方形结点。这样话,一棵树的枝干上将出现两个连续的叶子结点(这是不合常理的)。

也就是说字符串集合X中不存在一个串是另外一个串的前缀 。如何满足这个要求呢?我们可以在X中的每个串后面加入一个特殊字符$(这个字符将不会出现在字母表中)。这样,集合X{bear$、bell$、.... bi$、bid$}一定会满足这个要求。

总结:一个存储长度为n,来自大小为d的字母表中s个串的集合X的标准trie具有性质如下:

(1) 树中每个内部结点至多有d个子结点。

(2) 树有s个外部结点。

(3) 树的高度等于X中最长串的长度。

(4) 树中的结点数为O(n)。

标准 Trie树的查找

对于英文单词的查找,我们完全可以在内部结点中建立26个元素组成的指针数组。如果要查找a,只需要在内部节点的指针数组中找第0个指针即可(b=第1个指针,随机定位)。时间复杂度为O(1)。

查找过程:假如我们要在上面那棵Trie中查找字符串bull (b-u-l-l)。

(1) 在root结点中查找第('b'-'a'=1)号孩子指针,发现该指针不为空,则定位到第1号孩子结点处——b结点。

(2) 在b结点中查找第('u'-'a'=20)号孩子指针,发现该指针不为空,则定位到第20号孩子结点处——u结点。

(3) ... 一直查找到叶子结点出现特殊字符'$'位置,表示找到了bull字符串

如果在查找过程中终止于内部结点,则表示没有找到待查找字符串。

效率:对于有n个英文字母的串来说,在内部结点中定位指针所需要花费O(d)时间,d为字母表的大小,英文为26。由于在上面的算法中内部结点指针定位使用了数组随机存储方式,因此时间复杂度降为了O(1)。但是如果是中文字,下面在实际应用中会提到。因此我们在这里还是用O(d)。 查找成功的时候恰好走了一条从根结点到叶子结点的路径。因此时间复杂度为O(d*n)。

但是,当查找集合X中所有字符串两两都不共享前缀时,trie中出现最坏情况。除根之外,所有内部结点都自由一个子结点。此时的查找时间复杂度蜕化为O(d*(n^2))

标准 Trie树的Java代码实现:

  1. StandarTire.java
  2. Trie 树, 又称字典树,单词查找树。
  3. 它来源于retrieval(检索)中取中间四个字符构成(读音同try)。用于存储大量的字符串以便支持快速模式匹配。主要应用在信息检索领域。
  4. @author arhaiyun
  5. date:2013/09/23
  6. */
  7. import java.util.*;
  8. enum NodeKind{LN, BN};
  9. /**
  10. *Trie node
  11. */
  12. class TrieNode
  13. {
  14. char key;
  15. TrieNode[] points = null;
  16. NodeKind kind = null;
  17. }
  18. /**
  19. * Branch node
  20. */
  21. class BranchNode extends TrieNode
  22. {
  23. BranchNode(char k)
  24. {
  25. super.key = k;
  26. super.kind = NodeKind.BN;
  27. super.points = new TrieNode[27];
  28. }
  29. }
  30. /**
  31. * Leaf node
  32. */
  33. class LeafNode extends TrieNode
  34. {
  35. LeafNode(char k)
  36. {
  37. super.key = k;
  38. super.kind = NodeKind.LN;
  39. }
  40. }
  41. public class StandardTrie
  42. {
  43. //Create root node
  44. TrieNode root = new BranchNode(' ');
  45. //[1].Insert a word into tire tree
  46. public void insert(String words)
  47. {
  48. TrieNode curNode = root;
  49. //add '$' as an end symbol
  50. words = words + "$";
  51. char[] chars = words.toCharArray();
  52. for(int i = 0; i < chars.length; i++)
  53. {
  54. if(chars[i] == '$')
  55. {
  56. curNode.points[26] = new LeafNode('$');
  57. }
  58. else
  59. {
  60. int pSize = chars[i] - 'a';
  61. // If not exists creat a new branch node
  62. if(curNode.points[pSize] == null)
  63. {
  64. curNode.points[pSize] = new BranchNode(chars[i]);
  65. }
  66. curNode = curNode.points[pSize];
  67. }
  68. }
  69. }
  70. //[2].Check if a word is in tire tree
  71. public boolean fullMatch(String words)
  72. {
  73. TrieNode curNode = root;
  74. char[] chars = words.toCharArray();
  75. for(int i = 0; i < chars.length; i++)
  76. {
  77. int pSize = chars[i] - 'a';
  78. System.out.print(chars[i]+"->");
  79. if(curNode.points[pSize] == null)
  80. return false;
  81. curNode = curNode.points[pSize];
  82. }
  83. if(curNode.points[26] != null && curNode.points[26].key == '$')
  84. return true;
  85. return false;
  86. }
  87. //[3].preorder root traverse
  88. private void preorderTraverse(TrieNode curNode)
  89. {
  90. if(curNode != null)
  91. {
  92. System.out.print(curNode.key);
  93. if(curNode.kind == NodeKind.BN)
  94. {
  95. for(TrieNode node : curNode.points)
  96. {
  97. preorderTraverse(node);
  98. }
  99. }
  100. else
  101. System.out.println();
  102. }
  103. }
  104. //[4].Get root node
  105. public TrieNode getRoot()
  106. {
  107. return this.root;
  108. }
  109. public static void main(String[] args)
  110. {
  111. StandardTrie trie = new StandardTrie();
  112. trie.insert("amazon");
  113. trie.insert("yahoo");
  114. trie.insert("haiyun");
  115. trie.insert("baidu");
  116. trie.insert("alibaba");
  117. trie.insert("offer");
  118. trie.insert("stock");
  119. trie.insert("stop");
  120. trie.preorderTraverse(trie.getRoot());
  121. System.out.println(trie.fullMatch("yahoo"));
  122. System.out.println(trie.fullMatch("yaho"));
  123. System.out.println(trie.fullMatch("baidu"));
  124. System.out.println(trie.fullMatch("alibaba"));
  125. }
  126. }

Trie树(转:http://blog.csdn.net/arhaiyun/article/details/11913501)的更多相关文章

  1. 线段树详解 (原理,实现与应用)(转载自:http://blog.csdn.net/zearot/article/details/48299459)

    原文地址:http://blog.csdn.net/zearot/article/details/48299459(如有侵权,请联系博主,立即删除.) 线段树详解    By 岩之痕 目录: 一:综述 ...

  2. http://blog.csdn.net/v_july_v/article/details/6543438

    本文转载至: http://blog.csdn.net/v_july_v/article/details/6543438 算法 程序员面试.算法研究.编程艺术.红黑树.数据挖掘5大经典原创系列集锦与总 ...

  3. 转:Java面试题集(51-70) http://blog.csdn.net/jackfrued/article/details/17403101

    Java面试题集(51-70) Java程序员面试题集(51-70) http://blog.csdn.net/jackfrued/article/details/17403101 摘要:这一部分主要 ...

  4. 解析Javascript事件冒泡机制(转) 本文转自:http://blog.csdn.net/luanlouis/article/details/23927347

    本文转自:http://blog.csdn.net/luanlouis/article/details/23927347 1. 事件 在浏览器客户端应用平台,基本生都是以事件驱动的,即某个事件发生,然 ...

  5. 抽象类和接口有什么区别---https://blog.csdn.net/csdn_aiyang/article/details/71171886

    https://blog.csdn.net/csdn_aiyang/article/details/71171886 概念]   抽象类.具体类是相对的,并非绝对的.抽象是一种概念性名词,具体是一种可 ...

  6. http://blog.csdn.net/pizi0475/article/details/48286579 -------------(Collada 快速入门)

    http://blog.csdn.net/zhouhangjay/article/details/8469085 说明:Collada的文件格式,中文版的很少,在csdn上看到了一个Sleepy的,感 ...

  7. 转载 WPF -- 控件模板 (ControlTemplate)(一) https://blog.csdn.net/qq_23018459/article/details/79899838

    ControlTemplate(控件模板)   https://blog.csdn.net/qq_23018459/article/details/79899838 WPF包含数据模板和控件模板,其中 ...

  8. http://blog.csdn.net/java2000_wl/article/details/8627874

    http://blog.csdn.net/java2000_wl/article/details/8627874

  9. android 蓝牙 http://blog.csdn.net/u012843100/article/details/52384219

    http://blog.csdn.net/u012843100/article/details/52384219

随机推荐

  1. 终端IO(上)

    一.综述 终端IO有两种不同的工作方式: 规范方式输入处理.在这种方式中,终端输入以行为单位进行处理.对于每个读要求,终端驱动程序最多返回一行. 非规范方式输入处理.输入字符不以行为单位进行装配 如果 ...

  2. TOP100summit:【分享实录-美团点评】 业务快速升级发展背后的系统架构演进

    本篇文章内容来自2016年TOP100summit美团●大众点评高级技术专家,酒店后台研发组eHome团队负责人许关飞的案例分享.编辑:Cynthia 许关飞:美团●大众点评高级技术专家,酒店后台研发 ...

  3. 没有上司的舞会|codevs1380|luoguP1352|树形DP|Elena

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description Ural大学有N个职员,编号为1~N.他们有从属关系 ...

  4. pytorch的一些基本操作

    (1)生成一个未初始化的tensor import torch x = torch.Tensor(5,3) print(x) (2)随机初始化一个tensor y = torch.randn(5,3) ...

  5. 2018中国大学生程序设计竞赛 - 网络选拔赛 Dream hdu6440 Dream 给出一个(流氓)构造法

    http://acm.hdu.edu.cn/showproblem.php?pid=6440 题意:让你重新定义任意一对数的乘法和加法结果(输出乘法口诀表和加法口诀表),使得m^p+n^p==(m+n ...

  6. linux:基本指令mkdir, rmdir 和rm

    mkdir 建立文件夹 mkdir (make directory) 就是创建一个文件夹的意思, 使用起来很简单. 1.新建一个目录: $ mkdir folder2 如果你想在这个目录给 folde ...

  7. rsync定时同步文件

    rsync服务器 ip:192.168.1.198 操作系统:centos7.2 rsync客户端 ip:192.168.1.16 操作系统:centos7.2 服务器配置 1.yum -y inst ...

  8. 单KEY业务,数据库水平切分架构实践 | 架构师之路

    https://mp.weixin.qq.com/s/8aI9jS0SXJl5NdcM3TPYuQ 单KEY业务,数据库水平切分架构实践 | 架构师之路 原创: 58沈剑 架构师之路 2017-06- ...

  9. 如果是多个 c 代码的源码文件,编译方法如下: $ gcc test1.c test2.c -o main.out $ ./main.out test1.c 与 test2.c 是两个源代码文件。

    如果是多个 c 代码的源码文件,编译方法如下: $ gcc test1.c test2.c -o main.out $ ./main.out test1.c 与 test2.c 是两个源代码文件.

  10. 在计算机通信中,可靠交付应当由谁来负责?是网络还是端系统? 网络层协议 MAC帧、IP数据报、TCP报文 关系 IP地址与硬件地址 链路层与网络层

    小结: 1. 网络层两种服务 虚电路服务 virtual circuit  电信网 网络层负责可靠交付 数据报服务  网络层不负责可靠交付 提供灵活的.无连接的.尽最大努力交付的数据报服务 不提供服务 ...