前言
本文介绍前缀树的基本概念以及代码实现前缀树的基本功能。
正文
前缀树又称PrefixTree、Trie Tree、字典树,
- 在单个字符串中,字符从前到后的加到一棵多叉树上
- 字符放在路上,节点上有专属的数据项(场景的是
pass和end值) - 所有样本都这样添加,如果没有路就新建,如有路就复用
- 沿途节点的
pass值增加1,每个字符串结束时来到的节点end值增加1
可以完成前缀相关的查询
过程分析
假如有字符串组成的数组:["abc","abd","bce","abcd","bcf"],将这些字符串加入到一棵树里,加入过程如下:
遍历整个数组,对数组里每个字符串都进行遍历。
字符串"abc",先准备一个空节点,看这个节点有没有走向“a”的路,如果没有则新建一个节点,需要注意的是,字符不是放在节点上的,字符是放在路上的。然后依次创建走向b和c的路,如下
"abd",遍历第二个字符串,因为刚才已经创建了到a和到b的路,所以可以直接拿来复用,而b到d的路没有被创建过,所以只需要创建b到d的路即可,如图
同样的,字符串"abcd"和字符串"bcf"也按上述的步骤进行创建,如果路径已经被创建过直接复用,如果没有的话则进行新建,最终创建的路径图如下:
上面创建的前缀比较简单,节点都是空的,下面对节点做一些处理,给节点增加一些属性。
增加pass属性代表有多少路径经过了这个节点,增加end属性,代表这个节点是哪些路径的结尾。只有是有路径经过了某个节点,这个节点的pass属性就增加1,如果某个节点是某个路径的结束,那么end属性就增加1。
所以,增加pass和end属性后,上图路径结构就变为下图所示,方便起见pass使用p来表示,end使用e来表示。
经过上面分析之后,那么前缀树到底有什么用呢?
最简单的一个应用就是,从一个字符串集合中判断某个字符串出现过几次、或者有没有出现过。
例如在上图中,abcd这个字符串,从上图中按路径查找,找到d后的节点,p为1,e为1。说明abcd字符串出现过1次。再比如,abg这个字符串,在图中没有找到,说明该字符串没有出现过。
还可以用来判断,有多少字符串以某个字符串做前缀,例如,上图中有多少字符串以ab作为前缀呢?查找也很简单,也就是p的属性值,p是多少就代表有多少字符串是以这个作为前缀。
前缀树与哈希表相比,优势在于前缀树有可以找前缀的作用。
代码实现
下面以小写字符作为字符串字符,来看下前缀树的实现,首先来看下节点的定义:
public class Node {
public int pass;
public int end;
public Node[] nexts;
public Node() {
pass = 0;
end = 0;
nexts = new Node[26];
}
}
public class Trie {
private Node root;
public Trie() {
root = new Node();
}
}
在Node的构造函数初始化了26个路径,因为都是小写字母组成的,所以最大也就有26个路径。
将字符串加入前缀树的方法实现:
public void insert(String word) {
if (word == null) {
return;
}
char[] str = word.toCharArray();
Node node = root;
node.pass++;
int path = 0;
for (int i = 0; i < str.length; i++) {
// 由字符对应成走向哪条路,数组下标从0开始,所以减去a
path = str[i] - 'a';
if (node.nexts[path] == null) {
node.nexts[path] = new Node();
}
node = node.nexts[path];
node.pass++;
}
node.end++;
}
统计有多少以某个字符串作为前缀的数量:
public int prefixCount(String pre) {
if (pre == null) {
return 0;
}
char[] chs = pre.toCharArray();
Node node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
查找某个字符串出现了多少次:
public int search(String word) {
if (word == null) {
return 0;
}
char[] chs = word.toCharArray();
Node node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.end;
}
总结
本文介绍了前缀树的基本概念以及代码实现前缀树的基本功能:添加、搜索、前缀统计,本次只是介绍了前缀树的基本功能,其实前缀树还可以实现更复杂的功能。