什么是前缀树
- 前缀树又叫单词查找树,Tire树,是哈希树的变种。典型应用于统计,排序和保存大量字符串,经常被搜索引擎系统用于文本词频统计。
- 前缀树的每一个节点会有多个子节点,通往不同子节点的路径上有着不同的字符串(不仅限于字符串)。
- 优点:可以利用字符串的公共前缀来减少查询时间,最大限度地减少不必要的字符串比较,查询效率比哈希树高。
问题举例
一个字符串类型的数组arr1,另一个字符串类型数组arr2。
(1)arr2中有哪些字符串,是arr1中出现的?请打印。
(2)arr2中有哪些字符串,是作为arr1中某个字符串前缀出现的?请打印。
(3)arr2中有哪些字符串,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀。
构造前缀树
下面以数组String[] arr1={“ab”,“abc”,“bcde”,“aab”}; 为例构造一个前缀树
1.加入"ab"
2.加入"abc"
3.加入"bcde"
4.加入"aab"
前缀树的代码实现
public class TrieDemo {
public static void main(String[] args) {
String[] arr1={"ab","abc","bcde"};
TrieTree trieTree = new TrieTree();
trieTree.add("abc");
trieTree.add("abc");
trieTree.add("abcd");
trieTree.add("abcde");
trieTree.delete("abc");
int num = trieTree.search("abc");
System.out.println("num="+num);
int prefixNumber = trieTree.prefixNumber("abc");
System.out.println("prefixNumber="+prefixNumber);
}
//先构造一个类表示前缀树的结点
public static class TrieNode {
int path; //path表示经过这个结点的字符串个数(这个值不包含以这个结点为终点的字符串)
int end; //end表示在这个结点结束的字符串的个数
TrieNode[] nexts;
public TrieNode() {
this.path = 0;
this.end = 0;
nexts = new TrieNode[26];
}
}
//构造一个类表示前缀树
public static class TrieTree {
TrieNode root;
public TrieTree() {
//初始化根结点
this.root = new TrieNode();
}
//1.加入字符串的方法
public void add(String str) {
char[] chars = str.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chars.length; i++) {
index = chars[i] - 'a';
if (node.nexts[index] == null) { //说明没有这个结点
//创建一个新的结点
TrieNode trieNode = new TrieNode();
//让当前结点的nexts对应的结点指向这个新的结点
node.nexts[index] = trieNode;
}
//往下走,当前的node变成下一个node
node = node.nexts[index];
//把这个node的path+1
node.path++;
}
node.end++;
}
//2.查询前缀树中有几个这样的字符串
public int search(String str) {
if(str==null||str==""){
return 0;
}
char[] chars = str.toCharArray();
int index = 0;
TrieNode node = root;
for (int i = 0; i < chars.length; i++) {
index = chars[i] - 'a';
//如果找查找过程中node.nexts[index]为空表示chars还没有遍历完,说明树中没有这个字符串
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.end;
}
//3.删除字符串的方法
public void delete(String str) {
//1.先查询树中是否有这样一个字符串
//说明树中没有这样一个字符串
if (search(str) == 0) {
return;
}
TrieNode node = root;
char[] chars = str.toCharArray();
int index = 0;
for (int i = 0; i < chars.length; i++) {
index = chars[i] - 'a';
if (node.nexts[index].path == 0) { //这个时候说明node.nexts[index]这个结点上已经没有数据了,删除这个结点
node.nexts[index] = null;
return;
}
node = node.nexts[index];
node.path--;
}
node.end--;
}
//4.查询前缀树中有多少个以prefix为前缀的字符串
public int prefixNumber(String prefix) {
if (prefix == "" || prefix == null) {
return 0;
}
char[] chars = prefix.toCharArray();
int index = 0;
TrieNode node = root;
for (int i = 0; i < chars.length; i++) {
index = chars[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.path;
}
}
}