算法介绍
第一眼看到Trie树算法,首先明白的就是他一定是用树形结构实现的算法。后来实现完整个算法才知道其实他也是压缩树,类似于哈弗曼编码和CF-Tree,因为树中保留了公共的前缀,减少了不必要的重复存储空间。所以查询效率会高很多,如果你明白哈弗曼编码的实现过程,这个自然也是一样的道理。那Trie树与Huffman编码树有什么区别呢,Huffman是0或1的编码,而Trie则是文本查找树,节点上可以是一个字母字符,也可以是汉字等等,大体就是这个意思。好,下面说说算法的原理。
算法原理
1、首先获取所有的文本数据,划分成逐条逐条的形式。
2、读入每行数据,对照当前比较字符值与当前节点的子节点比较,寻找到与之匹配的节点
3、如果找到对应的子节点,将子节点作为当前节点,并移除数据的此字符,继续步骤2。
4、如果未找到对应子节点,新建节点插入当前的节点中,并将新节点作为当前节点,继续步骤2。
5、操作的终止条件为数据中的字符已经全部移除比较完毕。
算法实现
输入的字符数据Input.txt:
- abc
- bcd
- bca
- bcc
- bbd
- abca
- packageTrie;
- importjava.util.ArrayList;
- /**
- *
- *
- *
- *@authorlyq
- *
- *
- */
- publicclassTreeNode{
- //节点的值
- Stringvalue;
- //节点孩子节点
- ArrayList<TreeNode>childNodes;
- publicTreeNode(Stringvalue){
- this.value=value;
- this.childNodes=newArrayList<TreeNode>();
- }
- publicArrayList<TreeNode>getChildNodes(){
- returnchildNodes;
- }
- publicvoidsetChildNodes(ArrayList<TreeNode>childNodes){
- this.childNodes=childNodes;
- }
- }
- packageTrie;
- importjava.io.BufferedReader;
- importjava.io.File;
- importjava.io.FileReader;
- importjava.io.IOException;
- importjava.util.ArrayList;
- /**
- *
- *
- *
- *@authorlyq
- *
- *
- */
- publicclassTrieTool{
- //测试数据文件地址
- privateStringfilePath;
- //原始数据
- privateArrayList<String[]>datas;
- publicTrieTool(StringfilePath){
- this.filePath=filePath;
- readDataFile();
- }
- /**
- *
- *从文件中读取数据
- */
- privatevoidreadDataFile(){
- Filefile=newFile(filePath);
- ArrayList<String[]>dataArray=newArrayList<String[]>();
- try{
- BufferedReaderin=newBufferedReader(newFileReader(file));
- Stringstr;
- String[]tempArray;
- while((str=in.readLine())!=null){
- tempArray=newString[str.length()];
- for(inti=0;i<str.length();i++){
- tempArray[i]=str.charAt(i)+"";
- }
- dataArray.add(tempArray);
- }
- in.close();
- }catch(IOExceptione){
- e.getStackTrace();
- }
- datas=dataArray;
- }
- /**
- *
- *构造Trie树
- *
- *
- *
- *@return
- */
- publicTreeNodeconstructTrieTree(){
- TreeNoderootNode=newTreeNode(null);
- ArrayList<String>tempStr;
- for(String[]array:datas){
- tempStr=newArrayList<String>();
- for(Strings:array){
- tempStr.add(s);
- }
- //逐个字符串的添加
- addStrToTree(rootNode,tempStr);
- }
- returnrootNode;
- }
- /**
- *
- *添加字符串的内容到Trie树中
- *
- *
- *
- *@paramnode
- *
- *@paramstrArray
- */
- privatevoidaddStrToTree(TreeNodenode,ArrayList<String>strArray){
- booleanhasValue=false;
- TreeNodetempNode;
- TreeNodecurrentNode=null;
- //子节点中遍历寻找与当前第一个字符对应的节点
- for(TreeNodechildNode:node.childNodes){
- if(childNode.value.equals(strArray.get(0))){
- hasValue=true;
- currentNode=childNode;
- break;
- }
- }
- //如果没有找到对应节点,则将此节点作为新的节点
- if(!hasValue){
- //遍历到了未曾存在的字符值的,则新键节点作为当前节点的子节点
- tempNode=newTreeNode(strArray.get(0));
- //node.childNodes.add(tempNode);
- insertNode(node.childNodes,tempNode);
- currentNode=tempNode;
- }
- strArray.remove(0);
- //如果字符已经全部查找完毕,则跳出循环
- if(strArray.size()==0){
- return;
- }else{
- addStrToTree(currentNode,strArray);
- }
- }
- /**
- *
- *将新建的节点按照字母排序的顺序插入到孩子节点中
- *
- *
- *
- *@paramchildNodes
- *
- *孩子节点
- *
- *@paramnode
- *
- *新键的待插入的节点
- */
- privatevoidinsertNode(ArrayList<TreeNode>childNodes,TreeNodenode){
- Stringvalue=node.value;
- intinsertIndex=0;
- for(inti=0;i<childNodes.size()-1;i++){
- if(childNodes.get(i).value.compareTo(value)<=0
- &&childNodes.get(i+1).value.compareTo(value)>0){
- insertIndex=i+1;
- break;
- }
- }
- if(childNodes.size()==0){
- childNodes.add(node);
- }elseif(childNodes.size()==1){
- //只有1个的情况额外判断
- if(childNodes.get(0).value.compareTo(value)>0){
- childNodes.add(0,node);
- }else{
- childNodes.add(node);
- }
- }else{
- childNodes.add(insertIndex,node);
- }
- }
- }
- packageTrie;
- /**
- *
- *Trie树算法
- *
- *@authorlyq
- *
- *
- */
- publicclassClient{
- publicstaticvoidmain(String[]args){
- StringfilePath="C:\\Users\\lyq\\Desktop\\icon\\input.txt";
- TrieTooltool=newTrieTool(filePath);
- tool.constructTrieTree();
- }
- }
root
|
a b
| |---|
b b c
| | |----|-----|
c d a c d
|
a
算法的遗漏点和可以改进的地方
这里所说的遗漏点就是在插入节点的时候,需要按照字母的排序插入,这是为了使得查找更加的高效。算法在构建树的时候每次都从根节点开始往下找,效率不够高,其实更好的办法是把输入数据进行字典序的排序,然后再当前节点做处理,要么继续往下添加,要么回溯到上一个节点。
算法的特点
算法的特点在最开始介绍的时候也已经提到过,利用了字符串的公共前缀减少了查询时间,最大限度的减少无谓的字符串比较,常用于做文本的词频统计。