java字典树

什么是Trie 树呢?也就是常说的字典树,网上对此讲得也很多,简单补充一下个人理解: 它实际上相当于把单词的公共部分给拎出来,这样一层一层往上拎直到得到每个节点都是不可分的最小单元!

比如网上一个例子

一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:

这里的节点上存的是一个单词,实际上,每个节点走过的路径就是该节点代表的单词!其它不多扯了~~~

Trie树有什么好处呢

它是一种非常快的单词查询结构,当然,对于单词去重统计也是非常好的选择! 比如搜索引擎的关键词联想功能很好的一种选择就是使用Trie树了!比如你输入了in,通过上面的图我们应该提示inn和int ,这样可以轻松实现! 另外,对于单词出现的频率统计, 以及查找公共前缀等问题,都可以很好的解决! 本文不是讲理论,只是给出用java自己实现的Trie树数据结构,其中实现了 插入、查找、遍历、单词联想(找公共前缀) 等基本功能, 其它功能大家可以自己添加~~~~
以下是Trie树类:
  1. package com.algorithms;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class Trie_Tree{
  5. /**
  6. * 内部节点类
  7. * @author "zhshl"
  8. * @date 2014-10-14
  9. *
  10. */
  11. private class Node{
  12. private int dumpli_num; 该字串的重复数目, 该属性统计重复次数的时候有用,取值为0、1、2、3、4、5……
  13. private int prefix_num; ///以该字串为前缀的字串数, 应该包括该字串本身!!!!!
  14. private Node childs[]; 此处用数组实现,当然也可以map或list实现以节省空间
  15. private boolean isLeaf; ///是否为单词节点
  16. public Node(){
  17. dumpli_num= 0;
  18. prefix_num= 0;
  19. isLeaf= false;
  20. childs= new Node[ 26];
  21. }
  22. }
  23. private Node root; ///树根
  24. public Trie_Tree(){
  25. ///初始化trie 树
  26. root= new Node();
  27. }
  28. /**
  29. * 插入字串,用循环代替迭代实现
  30. * @param words
  31. */
  32. public void insert(String words){
  33. insert( this.root, words);
  34. }
  35. /**
  36. * 插入字串,用循环代替迭代实现
  37. * @param root
  38. * @param words
  39. */
  40. private void insert(Node root,String words){
  41. words=words.toLowerCase(); 转化为小写
  42. char[] chrs=words.toCharArray();
  43. for( int i= 0,length=chrs.length; i<length; i++){
  44. ///用相对于a字母的值作为下标索引,也隐式地记录了该字母的值
  45. int index=chrs[i]- 'a';
  46. if(root.childs[index]!= null){
  47. 已经存在了,该子节点prefix_num++
  48. root.childs[index].prefix_num++;
  49. } else{
  50. ///如果不存在
  51. root.childs[index]= new Node();
  52. root.childs[index].prefix_num++;
  53. }
  54. ///如果到了字串结尾,则做标记
  55. if(i==length- 1){
  56. root.childs[index].isLeaf= true;
  57. root.childs[index].dumpli_num++;
  58. }
  59. ///root指向子节点,继续处理
  60. root=root.childs[index];
  61. }
  62. }
  63. /**
  64. * 遍历Trie树,查找所有的words以及出现次数
  65. * @return HashMap<String, Integer> map
  66. */
  67. public HashMap<String,Integer> getAllWords(){
  68. // HashMap<String, Integer> map=new HashMap<String, Integer>();
  69. return preTraversal( this.root, "");
  70. }
  71. /**
  72. * 前序遍历。。。
  73. * @param root 子树根节点
  74. * @param prefixs 查询到该节点前所遍历过的前缀
  75. * @return
  76. */
  77. private HashMap<String,Integer> preTraversal(Node root,String prefixs){
  78. HashMap<String, Integer> map= new HashMap<String, Integer>();
  79. if(root!= null){
  80. if(root.isLeaf== true){
  81. 当前即为一个单词
  82. map.put(prefixs, root.dumpli_num);
  83. }
  84. for( int i= 0,length=root.childs.length; i<length;i++){
  85. if(root.childs[i]!= null){
  86. char ch=( char) (i+ 'a');
  87. 递归调用前序遍历
  88. String tempStr=prefixs+ch;
  89. map.putAll(preTraversal(root.childs[i], tempStr));
  90. }
  91. }
  92. }
  93. return map;
  94. }
  95. /**
  96. * 判断某字串是否在字典树中
  97. * @param word
  98. * @return true if exists ,otherwise false
  99. */
  100. public boolean isExist(String word){
  101. return search( this.root, word);
  102. }
  103. /**
  104. * 查询某字串是否在字典树中
  105. * @param word
  106. * @return true if exists ,otherwise false
  107. */
  108. private boolean search(Node root,String word){
  109. char[] chs=word.toLowerCase().toCharArray();
  110. for( int i= 0,length=chs.length; i<length;i++){
  111. int index=chs[i]- 'a';
  112. if(root.childs[index]== null){
  113. ///如果不存在,则查找失败
  114. return false;
  115. }
  116. root=root.childs[index];
  117. }
  118. return true;
  119. }
  120. /**
  121. * 得到以某字串为前缀的字串集,包括字串本身! 类似单词输入法的联想功能
  122. * @param prefix 字串前缀
  123. * @return 字串集以及出现次数,如果不存在则返回null
  124. */
  125. public HashMap<String, Integer> getWordsForPrefix(String prefix){
  126. return getWordsForPrefix( this.root, prefix);
  127. }
  128. /**
  129. * 得到以某字串为前缀的字串集,包括字串本身!
  130. * @param root
  131. * @param prefix
  132. * @return 字串集以及出现次数
  133. */
  134. private HashMap<String, Integer> getWordsForPrefix(Node root,String prefix){
  135. HashMap<String, Integer> map= new HashMap<String, Integer>();
  136. char[] chrs=prefix.toLowerCase().toCharArray();
  137. for( int i= 0, length=chrs.length; i<length; i++){
  138. int index=chrs[i]- 'a';
  139. if(root.childs[index]== null){
  140. return null;
  141. }
  142. root=root.childs[index];
  143. }
  144. ///结果包括该前缀本身
  145. ///此处利用之前的前序搜索方法进行搜索
  146. return preTraversal(root, prefix);
  147. }
  148. }

以下是测试类:
  1. package com.algorithm.test;
  2. import java.util.HashMap;
  3. import com.algorithms.Trie_Tree;
  4. public class Trie_Test {
  5. public static void main(String args[]) //Just used for test
  6. {
  7. Trie_Tree trie = new Trie_Tree();
  8. trie.insert( "I");
  9. trie.insert( "Love");
  10. trie.insert( "China");
  11. trie.insert( "China");
  12. trie.insert( "China");
  13. trie.insert( "China");
  14. trie.insert( "China");
  15. trie.insert( "xiaoliang");
  16. trie.insert( "xiaoliang");
  17. trie.insert( "man");
  18. trie.insert( "handsome");
  19. trie.insert( "love");
  20. trie.insert( "chinaha");
  21. trie.insert( "her");
  22. trie.insert( "know");
  23. HashMap<String,Integer> map=trie.getAllWords();
  24. for(String key:map.keySet()){
  25. System.out.println(key+ " 出现: "+ map.get(key)+ "次");
  26. }
  27. map=trie.getWordsForPrefix( "chin");
  28. System.out.println( "\n\n包含chin(包括本身)前缀的单词及出现次数:");
  29. for(String key:map.keySet()){
  30. System.out.println(key+ " 出现: "+ map.get(key)+ "次");
  31. }
  32. if(trie.isExist( "xiaoming")== false){
  33. System.out.println( "\n\n字典树中不存在:xiaoming ");
  34. }
  35. }
  36. }


运行结果:

love 出现: 2次
chinaha 出现: 1次
her 出现: 1次
handsome 出现: 1次
know 出现: 1次
man 出现: 1次
xiaoliang 出现: 2次
i 出现: 1次
china 出现: 5次


包含chin(包括本身)前缀的单词及出现次数:
chinaha 出现: 1次
china 出现: 5次


字典树中不存在:xiaoming 

总结:在实现的时候,主要是想好如何设计每个节点的结构,这里针对单词总共26个,使用了一个字符数组来记录,其实完全可以用list或其他的容器来实现,这样也就可以容纳更复杂的对象了!另外一个方面就是,一个节点的prefix_num属性实际上是指到该节点经过的路径(也就是字串)的重复数,而不是到该节点的重复数(因为一个节点的child域并不是指某个单词,这样prefix_num对该节点本身没意义)。最后,遍历使用了前序遍历的递归实现。相信对学过一点数据结构的不难。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值