前缀树的结构
Trie树,又叫字典树、前缀树(Prefix Tree)、单词查找树或键树,是一种多叉树结构。如下图:
上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。
Trie树的基本性质:
- 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符互不相同。
- 从第一字符开始有连续重复的字符只占用一个节点,比如上面的to,和ten,中重复的单词t只占用了一个节点。
前缀树的应用
- 前缀匹配
- 字符串检索
- 词频统计
- 字符串排序
前缀匹配
前缀单词:in,就是 inn 的前缀单词。如果有十几万条单词,并且每个单词的长度都是5-10以内,这样必定存在大量重复的
字符,因此利用前缀树来求解不仅速度快而且空间复杂度也比较好。
代码实现
public class TrieTools {
private TrieNode root = new TrieNode();
/**
* 字典树的加入过程
*/
public void add(String word) {
if (word == null)
return;
char[] chars = word.toCharArray();
TrieNode node = root;
for (char c : chars) {
// 当前节点的子节点不存在这个字符,子节点添加该字符
if (!node.children.containsKey(c)) {
node.children.put(c, new TrieNode());
}
// 当前节点的子节点存在这个字符,下一个元素位置在子节点的子节点
node = node.children.get(c);
// 字符路过这个结点的次数+1
node.path++;
}
// 以当前结点为结束的字符+1
node.end++;
}
/**
* 字典树查询目标单词出现的次数
*/
public int get(String word) {
//排除null情况
if (word == null)return 0;
char[] chars = word.toCharArray();
TrieNode node = root;
//查找单词的最后一个节点,该节点记录了这个单词出现的次数
for (char c : chars) {
//如果首字符串不在节点中,返回0
if (!node.children.containsKey(c))return 0;
node = node.children.get(c);
}
return node.end;
}
/**
* 字典树查询以目标前缀的单词有多少个
*/
public int getPre(String word) {
//排除null情况
if (word == null)return 0;
char[] chars = word.toCharArray();
TrieNode node = root;
//查找单词的最后一个节点,该节点记录了这个字符出现的次数,也就是目标前缀的单词的数目
for (char c : chars) {
//如果首字符串不在节点中,返回0
if (!node.children.containsKey(c))return 0;
node = node.children.get(c);
}
return node.path;
}
public static void main(String[] args) {
TrieTools trie = new TrieTools();
trie.add("a");
trie.add("ab");
trie.add("ac");
trie.add("abc");
trie.add("acb");
trie.add("abcc");
trie.add("aab");
trie.add("abx");
System.out.println("单词出现的次数: "+trie.get("abc"));
System.out.println("目标前缀的单词数: "+trie.getPre("ab"));
}
}
/**
* 前缀树结构
*/
public class TrieNode {
/**
* path表示字符路过这个结点的次数(即表示存在以当前结点为前缀的字符有多少个);
*/
public int path;
/**
* end记录以当前结点为结束的字符有多少个。
*/
public int end;
/**
* 子节点
*/
HashMap<Character, TrieNode> children=new HashMap<Character, TrieNode>();
}
测试结果:
单词出现的次数: 1
目标前缀的单词数: 4
过程步骤: