Trie树算法

算法介绍

第一眼看到Trie树算法,首先明白的就是他一定是用树形结构实现的算法。后来实现完整个算法才知道其实他也是压缩树,类似于哈弗曼编码和CF-Tree,因为树中保留了公共的前缀,减少了不必要的重复存储空间。所以查询效率会高很多,如果你明白哈弗曼编码的实现过程,这个自然也是一样的道理。那Trie树与Huffman编码树有什么区别呢,Huffman是0或1的编码,而Trie则是文本查找树,节点上可以是一个字母字符,也可以是汉字等等,大体就是这个意思。好,下面说说算法的原理。

算法原理

1、首先获取所有的文本数据,划分成逐条逐条的形式。

2、读入每行数据,对照当前比较字符值与当前节点的子节点比较,寻找到与之匹配的节点

3、如果找到对应的子节点,将子节点作为当前节点,并移除数据的此字符,继续步骤2。

4、如果未找到对应子节点,新建节点插入当前的节点中,并将新节点作为当前节点,继续步骤2。

5、操作的终止条件为数据中的字符已经全部移除比较完毕。

算法实现

输入的字符数据Input.txt:

  1. abc
  2. bcd
  3. bca
  4. bcc
  5. bbd
  6. abca
树节点类TreeNode.java:

  1. packageTrie;
  2. importjava.util.ArrayList;
  3. /**
  4. *
  5. *
  6. *
  7. *@authorlyq
  8. *
  9. *
  10. */
  11. publicclassTreeNode{
  12. //节点的值
  13. Stringvalue;
  14. //节点孩子节点
  15. ArrayList<TreeNode>childNodes;
  16. publicTreeNode(Stringvalue){
  17. this.value=value;
  18. this.childNodes=newArrayList<TreeNode>();
  19. }
  20. publicArrayList<TreeNode>getChildNodes(){
  21. returnchildNodes;
  22. }
  23. publicvoidsetChildNodes(ArrayList<TreeNode>childNodes){
  24. this.childNodes=childNodes;
  25. }
  26. }
算法工具类TrieTool.java:

  1. packageTrie;
  2. importjava.io.BufferedReader;
  3. importjava.io.File;
  4. importjava.io.FileReader;
  5. importjava.io.IOException;
  6. importjava.util.ArrayList;
  7. /**
  8. *
  9. *
  10. *
  11. *@authorlyq
  12. *
  13. *
  14. */
  15. publicclassTrieTool{
  16. //测试数据文件地址
  17. privateStringfilePath;
  18. //原始数据
  19. privateArrayList<String[]>datas;
  20. publicTrieTool(StringfilePath){
  21. this.filePath=filePath;
  22. readDataFile();
  23. }
  24. /**
  25. *
  26. *从文件中读取数据
  27. */
  28. privatevoidreadDataFile(){
  29. Filefile=newFile(filePath);
  30. ArrayList<String[]>dataArray=newArrayList<String[]>();
  31. try{
  32. BufferedReaderin=newBufferedReader(newFileReader(file));
  33. Stringstr;
  34. String[]tempArray;
  35. while((str=in.readLine())!=null){
  36. tempArray=newString[str.length()];
  37. for(inti=0;i<str.length();i++){
  38. tempArray[i]=str.charAt(i)+"";
  39. }
  40. dataArray.add(tempArray);
  41. }
  42. in.close();
  43. }catch(IOExceptione){
  44. e.getStackTrace();
  45. }
  46. datas=dataArray;
  47. }
  48. /**
  49. *
  50. *构造Trie树
  51. *
  52. *
  53. *
  54. *@return
  55. */
  56. publicTreeNodeconstructTrieTree(){
  57. TreeNoderootNode=newTreeNode(null);
  58. ArrayList<String>tempStr;
  59. for(String[]array:datas){
  60. tempStr=newArrayList<String>();
  61. for(Strings:array){
  62. tempStr.add(s);
  63. }
  64. //逐个字符串的添加
  65. addStrToTree(rootNode,tempStr);
  66. }
  67. returnrootNode;
  68. }
  69. /**
  70. *
  71. *添加字符串的内容到Trie树中
  72. *
  73. *
  74. *
  75. *@paramnode
  76. *
  77. *@paramstrArray
  78. */
  79. privatevoidaddStrToTree(TreeNodenode,ArrayList<String>strArray){
  80. booleanhasValue=false;
  81. TreeNodetempNode;
  82. TreeNodecurrentNode=null;
  83. //子节点中遍历寻找与当前第一个字符对应的节点
  84. for(TreeNodechildNode:node.childNodes){
  85. if(childNode.value.equals(strArray.get(0))){
  86. hasValue=true;
  87. currentNode=childNode;
  88. break;
  89. }
  90. }
  91. //如果没有找到对应节点,则将此节点作为新的节点
  92. if(!hasValue){
  93. //遍历到了未曾存在的字符值的,则新键节点作为当前节点的子节点
  94. tempNode=newTreeNode(strArray.get(0));
  95. //node.childNodes.add(tempNode);
  96. insertNode(node.childNodes,tempNode);
  97. currentNode=tempNode;
  98. }
  99. strArray.remove(0);
  100. //如果字符已经全部查找完毕,则跳出循环
  101. if(strArray.size()==0){
  102. return;
  103. }else{
  104. addStrToTree(currentNode,strArray);
  105. }
  106. }
  107. /**
  108. *
  109. *将新建的节点按照字母排序的顺序插入到孩子节点中
  110. *
  111. *
  112. *
  113. *@paramchildNodes
  114. *
  115. *孩子节点
  116. *
  117. *@paramnode
  118. *
  119. *新键的待插入的节点
  120. */
  121. privatevoidinsertNode(ArrayList<TreeNode>childNodes,TreeNodenode){
  122. Stringvalue=node.value;
  123. intinsertIndex=0;
  124. for(inti=0;i<childNodes.size()-1;i++){
  125. if(childNodes.get(i).value.compareTo(value)<=0
  126. &&childNodes.get(i+1).value.compareTo(value)>0){
  127. insertIndex=i+1;
  128. break;
  129. }
  130. }
  131. if(childNodes.size()==0){
  132. childNodes.add(node);
  133. }elseif(childNodes.size()==1){
  134. //只有1个的情况额外判断
  135. if(childNodes.get(0).value.compareTo(value)>0){
  136. childNodes.add(0,node);
  137. }else{
  138. childNodes.add(node);
  139. }
  140. }else{
  141. childNodes.add(insertIndex,node);
  142. }
  143. }
  144. }
测试类Client.java:

  1. packageTrie;
  2. /**
  3. *
  4. *Trie树算法
  5. *
  6. *@authorlyq
  7. *
  8. *
  9. */
  10. publicclassClient{
  11. publicstaticvoidmain(String[]args){
  12. StringfilePath="C:\\Users\\lyq\\Desktop\\icon\\input.txt";
  13. TrieTooltool=newTrieTool(filePath);
  14. tool.constructTrieTree();
  15. }
  16. }
算法的最终构造的树的形状大致如下(由于时间关系,我就没有写在控制台输出的程序了):

root

|

a b

| |---|

b b c

| | |----|-----|

c d a c d

|

a

算法的遗漏点和可以改进的地方

这里所说的遗漏点就是在插入节点的时候,需要按照字母的排序插入,这是为了使得查找更加的高效。算法在构建树的时候每次都从根节点开始往下找,效率不够高,其实更好的办法是把输入数据进行字典序的排序,然后再当前节点做处理,要么继续往下添加,要么回溯到上一个节点。

算法的特点

算法的特点在最开始介绍的时候也已经提到过,利用了字符串的公共前缀减少了查询时间,最大限度的减少无谓的字符串比较,常用于做文本的词频统计。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值