Trie树分析
Posted 汤高
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Trie树分析相关的知识,希望对你有一定的参考价值。
Trie树
Trie树介绍
Trie,又称单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
它有3个基本性质:
1.根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2.从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3.每个节点的所有子节点包含的字符都不相同。
Trie中每个节点有一个特殊标记作为结束符号,通过该标记可以判断当前节点是否是一个字符串的终结节点。
下图是一个Trie树的例子,记录了to,tea,ted,ten,a,i,in,inn这些words(以蓝色结尾)。
个人总结了如下方法:
Trie树的节点类
class Node
//节点字符值
private char val;
//节点的子节点,即以根节点到该节点的值组成的字符串为前缀的字符串构建的节点
private Map<Character,Node> childrens=new HashMap<Character,Node>();
//是否为结束节点,即一个字符串是否到达末尾节点 当end>0时表示结束节点
private int end=0;
//从根节点到该结束节点组成的字符串的重复数量 即单词列表中每个单词的词频
private int dumpliNum=0;
//以到达该节点组成的子串为前缀的字符串数量
private int prexNum=0;
/**
* @return the dumpliNum
*/
public int getDumpliNum()
return dumpliNum;
/**
* @param dumpliNum the dumpliNum to set
*/
public void setDumpliNum(int dumpliNum)
this.dumpliNum = dumpliNum;
/**
* @return the prexNum
*/
public int getPrexNum()
return prexNum;
/**
* @param prexNum the prexNum to set
*/
public void setPrexNum(int prexNum)
this.prexNum = prexNum;
/**
* @return the val
*/
public char getVal()
return val;
/**
* @param val the val to set
*/
public void setVal(char val)
this.val = val;
/**
* @return the childrens
*/
public Map<Character, Node> getChildrens()
return childrens;
/**
* @param childrens the childrens to set
*/
public void setChildrens(Map<Character, Node> childrens)
this.childrens = childrens;
/**
* @return the end
*/
public int getEnd()
return end;
/**
* @param end the end to set
*/
public void setEnd(int end)
this.end = end;
Trie树的初始化
public class Trie
//待构建的字符串列表
private String words[];
private Node root;
public Trie(String[] words)
this.root=new Node();
this.words=words;
Trie树的插入
//插入方法
public void insert(String word)
if(word==""||word==null)
return;
Node cur=root;
char [] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
//如果不存在该值,则创建新节点
char val=chars[i];
if(!cur.getChildrens().containsKey(val))
Node newNode=new Node();
newNode.setVal(val);
cur.getChildrens().put(val,newNode);
//如果已经存在则pass
cur=cur.getChildrens().get(val);
cur.setPrexNum(cur.getPrexNum()+1);
cur.setEnd(1);
cur.setDumpliNum(cur.getDumpliNum()+1);
Trie树的构建
public Node builtTrie()
for (int i=0;i<words.length;i++)
insert(words[i]);
return root;
Trie树的查找
public boolean search(String word)
boolean flag=true;
Node cur=root;
char[] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
if(!cur.getChildrens().containsKey(chars[i]))
returnfalse;
cur=cur.getChildrens().get(chars[i]);//更新游标
return flag&&cur.getEnd()>0;//并且字符串最后一个字符必须是结束节点
Trie树中是否包某个前缀
//以某个字符串开头 比如字符串列表中有[abb,abbb],则ab返回true
public boolean startWith(String word)
boolean flag=true;
Node cur=root;
char[] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
if(!cur.getChildrens().containsKey(chars[i]))
return false;
cur=cur.getChildrens().get(chars[i]);//更新游标
//return flag&&cur.getEnd()>0;//并且该节点是结束节点
return flag;//和查找相比去掉了字符串最后一个字符要是结束节点
Trie树前序遍历
// 前序遍历,获取从指定节点开始,到所有结束节点组成的字符串
Map<String, Node> preTraversal(Nodenode,String word)
Map<String, Node> m=new HashMap<String,Node>();
if(node!=null)
if(node!=root&&node.getEnd()>0)
m.put(word, node);
for(Map.Entry<Character, Node>entry:node.getChildrens().entrySet())
Node cur=entry.getValue();
String temp=word+String.valueOf(cur.getVal());
m.putAll(preTraversal(cur,temp));
return m;
获取指定前缀出现次数
//获取指定前缀出现次数
int countPrefix(String prefix)
Node cur =root;
char[] chars=prefix.toCharArray();
while(cur.getChildrens().size()>0)
for(int i=0;i<chars.length;i++)
if(cur.getChildrens().containsKey(chars[i]))
cur=cur.getChildrens().get(chars[i]);
return cur.getPrexNum();
return 0;
获取含有指定前缀的所有单词
//获取含有指定前缀的所有单词
Map<String,Node>containPrefixOfwords(String prefix)
Map<String,Node> m=newHashMap<String,Node>();
//找到前缀的最后一个单词的所在节点
Node cur=root;
char[] chars=prefix.toCharArray();
while(cur.getChildrens().size()>0)
for(int i=0;i<chars.length;i++)
if(cur.getChildrens().containsKey(chars[i]))
cur=cur.getChildrens().get(chars[i]);
else
return null;
break;
m.putAll(preTraversal(cur,prefix));
return m;
全部代码和测试类
package trie;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
*
*Trie
* Trie树
*@author: 汤高
*@date: :2018-1-8 下午4:33:44
*@version 1.0
*/
public class Trie
//待构建的字符串列表
private String words[];
private Node root;
public Trie(String[] words)
this.root=new Node();
this.words=words;
public Node builtTrie()
int index=0;
for (int i=0;i<words.length;i++)
insert(words[i],++index);
return root;
//插入方法
public void insert(String word)
if(word==""||word==null)
return;
Node cur=root;
char [] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
//如果不存在该值,则创建新节点
char val=chars[i];
if(!cur.getChildrens().containsKey(val))
Node newNode=new Node();
newNode.setVal(val);
cur.getChildrens().put(val, newNode);
//如果已经存在则pass
cur=cur.getChildrens().get(val);
cur.setPrexNum(cur.getPrexNum()+1);
cur.setEnd(1);
cur.setDumpliNum(cur.getDumpliNum()+1);
//插入方法
public void insert(String word,int index)
if(word==""||word==null)
return;
Node cur=root;
char [] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
//如果不存在该值,则创建新节点
char val=chars[i];
if(!cur.getChildrens().containsKey(val))
Node newNode=new Node();
newNode.setVal(val);
cur.getChildrens().put(val, newNode);
//如果已经存在则pass
cur=cur.getChildrens().get(val);
cur.setPrexNum(cur.getPrexNum()+1);
cur.setEnd(index);
cur.setDumpliNum(cur.getDumpliNum()+1);
//查找方法
public boolean search(String word)
boolean flag=true;
Node cur=root;
char[] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
if(!cur.getChildrens().containsKey(chars[i]))
return false;
cur=cur.getChildrens().get(chars[i]);//更新游标
return flag&&cur.getEnd()>0;//并且字符串最后一个字符必须是结束节点
//以某个字符串开头 比如 字符串列表中有[abb,abbb],则ab返回true
public boolean startWith(String word)
boolean flag=true;
Node cur=root;
char[] chars=word.toCharArray();
for(int i=0;i<chars.length;i++)
if(!cur.getChildrens().containsKey(chars[i]))
return false;
cur=cur.getChildrens().get(chars[i]);//更新游标
//return flag&&cur.getEnd()>0;//并且该节点是结束节点
return flag;//和查找相比去掉了 字符串最后一个字符要是结束节点
//前序遍历
Map<String, Node> preTraversal(Node node,String word)
Map<String, Node> m=new HashMap<String, Node>();
if(node!=null)
if(node!=root&&node.getEnd()>0)
m.put(word, node);
for(Map.Entry<Character, Node> entry:node.getChildrens().entrySet())
Node cur=entry.getValue();
String temp=word+String.valueOf(cur.getVal());
m.putAll(preTraversal(cur,temp));
return m;
//获取指定前缀出现次数
int countPrefix(String prefix)
Node cur =root;
char[] chars=prefix.toCharArray();
while(cur.getChildrens().size()>0)
for(int i=0;i<chars.length;i++)
if(cur.getChildrens().containsKey(chars[i]))
cur=cur.getChildrens().get(chars[i]);
return cur.getPrexNum();
return 0;
//获取含有指定前缀的所有单词
Map<String,Node> containPrefixOfwords(String prefix)
Map<String,Node> m=new HashMap<String,Node>();
//找到前缀的最后一个单词的所在节点
Node cur=root;
char[] chars=prefix.toCharArray();
while(cur.getChildrens().size()>0)
for(int i=0;i<chars.length;i++)
if(cur.getChildrens().containsKey(chars[i]))
cur=cur.getChildrens().get(chars[i]);
else
return null;
break;
m.putAll(preTraversal(cur,prefix));
return m;
String getMax()
String result="";
Stack<Node> stack=new Stack<Node>();
stack.push(root);
while(!stack.isEmpty())
Node node=stack.pop();
if(node.getEnd()>0||node==root)
if(node!=root)
String word= this.words[node.getEnd()-1];
if(word.length()>result.length()||
word.length()==result.length()&&result.compareTo(word)>0)//最长的且具有最小字典序的word
result=word;
for(Node n:node.getChildrens().values())//拿到子节点
stack.push(n);
return result;
class Node
//节点字符值
private char val;
//节点的子节点,即以根节点到该节点的值组成的字符串为前缀的字符串构建的节点
private Map<Character,Node> childrens=new HashMap<Character,Node>();
//是否为结束节点,即一个字符串是否到达末尾节点 当end>0时表示结束节点
private int end=0;
//从根节点到该结束节点组成的字符串的重复数量 即单词列表中每个单词的词频
private int dumpliNum=0;
//以到达该节点组成的子串为前缀的字符串数量
private int prexNum=0;
/**
* @return the dumpliNum
*/
public int getDumpliNum()
return dumpliNum;
/**
* @param dumpliNum the dumpliNum to set
*/
public void setDumpliNum(int dumpliNum)
this.dumpliNum = dumpliNum;
/**
* @return the prexNum
*/
public int getPrexNum()
return prexNum;
/**
* @param prexNum the prexNum to set
*/
public void setPrexNum(int prexNum)
this.prexNum = prexNum;
/**
* @return the val
*/
public char getVal()
return val;
/**
* @param val the val to set
*/
public void setVal(char val)
this.val = val;
/**
* @return the childrens
*/
public Map<Character, Node> getChildrens()
return childrens;
/**
* @param childrens the childrens to set
*/
public void setChildrens(Map<Character, Node> childrens)
this.childrens = childrens;
/**
* @return the end
*/
public int getEnd()
return end;
/**
* @param end the end to set
*/
public void setEnd(int end)
this.end = end;
public static void main(String[] args)
String[] words=new String[]"ab","abb","ab","abbc","b","ba","bdd";
Trie t=new Trie(words);
Node root=t.builtTrie();//构建trie树
System.out.println("trie树包含bdd吗? "+t.search("bdd"));
System.out.println("trie树包含bd吗? "+t.search("bd"));
System.out.println("trie树包含bd前缀吗?"+t.startWith("bd"));
System.out.println("trie树包含bdX前缀吗?"+t.startWith("bdX"));
System.out.println("根节点前序遍历,获取所有单词和它出现的次数");
for(Map.Entry<String, Node> en:t.preTraversal(root, "").entrySet())
System.out.println(en.getKey()+"出现几次:"+en.getValue().dumpliNum);
System.out.println("以ab开头的前缀出现几次:"+t.countPrefix("ab"));
System.out.println("包含ab为前缀的所有单词");
for(Map.Entry<String, Node> en:t.containPrefixOfwords("ab").entrySet())
System.out.println(en.getKey()+"单词出现几次:"+en.getValue().dumpliNum);
测试结果
trie树包含bdd吗? true
trie树包含bd吗? false
trie树包含bd前缀吗?true
trie树包含bdX前缀吗?false
根节点前序遍历,获取所有单词和它出现的次数
abb出现几次:1
b出现几次:1
ba出现几次:1
bdd出现几次:1
abbc出现几次:1
ab出现几次:2
以ab开头的前缀出现几次:4
包含ab为前缀的所有单词
abb单词出现几次:1
ab单词出现几次:2
abbc单词出现几次:1
转载请指明http://blog.csdn.net/tanggao1314/article/details/79004795
以上是关于Trie树分析的主要内容,如果未能解决你的问题,请参考以下文章