字典树
1.1 基本数据结构
数据结构大都离不开数组和链表。他们是计算机中最基本的数据机构,基本所有的数据机构都可以有这两种数据机构来实现。
而链表是一种特殊的树,树更是一种特殊的图。
极客时间 数学专栏的作者的一段话,一直觉得很受用:
其实计算机到了最后还是搞数学,我们要做的就是如何将问题应用到数学在到编程上去。
数学思想到编程的实现大都分为三步走
- 把问题转化为数学中的模型
- 用数据结构与算法来刻画数学模型
- 落实编码
1.2 字典树
1.2.1 什么是字典树?
字典树也可以叫做前缀树,其实就是将很多单词组成的一种数据机构。通常都是树形结构的所有叫做字典树。
如下图所示:

跟节点通常都是空,不同的单词组成一条条的通路,形成了一个树状结构。
字典树有两种实现方式链表和数组。
因为字典树的二维关系是非常稀疏非常动态的,所以采用链表来实现字典树。
具体的代码注释如下:
static class TreeNode{
/**
* 节点的数据void
*/
private char data;
/**
* 子节点
*/
private Map<Character,TreeNode> sons = null;
/**
* 节点的解释
*/
private String explanation = null;
/**
* 节点的前缀字符串
*/
private String prefix = null;
public TreeNode(char data, String explanation, String prefix) {
this.data = data;
this.sons = new HashMap<>(16);
this.explanation = explanation;
this.prefix = prefix;
}
}
1.2.2 字典树的操作
字典树有两种
因为字典树的二维关系是非常稀疏非常动态的,
字典树的操作有两个
-
构建字典树(Dynamic)
public static void buildPrefixTree(String word){ if(StringUtils.isEmpty(word)) { return; } TreeNode findNode = headNode; String prefix = ""; String explanation = null; for(int i=0;i<word.length();i++){ char c = word.charAt(i); Map<Character, TreeNode> sons = findNode.sons; if(sons.containsKey(c)){ findNode = sons.get(c); }else{ if(i == word.length()-1){ explanation = prefix + c; } TreeNode sonNode = new TreeNode(c,explanation,prefix); sons.put(c,sonNode); findNode = sonNode; } prefix += c; } }
-
查询字典
/**
* 在PrefixTree中查找 单词
* @param word
* @return
*/
public static TreeNode findWrod(String word){
if(StringUtils.isEmpty(word)) {
return null;
}
TreeNode findNode = headNode;
for(int i=0;i<word.length();i++){
char c = word.charAt(i);
Map<Character, TreeNode> sons = findNode.sons;
if( sons.size()!=0 && sons.containsKey(c)){
findNode = sons.get(c);
}else{
return null;
}
}
if(StringUtils.isEmpty(findNode.explanation)){
return null;
}
return findNode;
}
-
遍历字典树中的所有单词
/** * 使用栈便利所有的单词 */ public static void dfsByStack(){ Stack<TreeNode> stack = new Stack<>(); stack.push(headNode); while(stack.size()>0){ TreeNode node = stack.pop(); if(!StringUtils.isEmpty(node.explanation)){ System.out.println(node.explanation); } Map<Character, TreeNode> sons = node.sons; sons.forEach((sonKey,sonValue)->{ stack.push(sonValue); }); } }
字典树的这三种操作其实都可以用递归来实现,但是最后一种的效率是明显优于递归的,因为减少了大量的中间变量的创建,利用了栈模拟了递归调用。