Trie树(转)

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代码实现:

[java] view plaincopy

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

Trie树(转)的相关文章

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

Trie树学习2

数组实现的Trie树 字符容量有限,可以使用链表实现更为大容量的Trie #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> #include <cstdlib> #

trie树(字典树)

1. trie树,又名字典树,顾名思义,它是可以用来作字符串查找的数据结构,它的查找效率比散列表还要高. trie树的建树: 比如有字符串"ab" ,"adb","adc"   可以建立字典树如图: 树的根节点head不存储信息,它有26个next指针,分别对应着字符a,b,c等.插入字符串ab时,next['a'-'a']即next[0]为空,这是申请一个结点放在next[0]的位置,插入字符串db时,next['d'-'a']即next[3]

Trie树

Trie树,即字典树或单词查找树,主要用于大量字符串的检索.去重.排序等操作. 主要原理就是利用字符串的公共前缀建立一棵多叉树,牺牲空间换取时间. 1 //Trie树 2 #include <iostream> 3 #include <string> 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 using std::string; 8 9 const int SIZE = 26; 10 const char B

bzoj4103异或运算 可持久化trie树

要去清华冬令营了,没找到2016年的题,就先坐一坐15年的. 因为n很小,就按照b串建可持久化trie树,a串暴力枚举. 其他的直接看代码. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; inline int read() { int x=0,f=1,ch=getchar(); while(ch<'0'||ch

hihoCoder 1014 Trie树

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

HDU 1251 Trie树模板题

1.HDU 1251 统计难题  Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b #define F(i,a,b

hiho一下 第二周&amp;第四周:从Trie树到Trie图

hihocoder #1014 题目地址:http://hihocoder.com/problemset/problem/1014 hihocoder #1036 题目地址: http://hihocoder.com/problemset/problem/1036 trie图其实就是trie树+KMP #1014trie树 #include<stdio.h> #include <algorithm> #include <cstring> #include <str

剑指Offer——Trie树(字典树)

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