目录
- 1.概述
- 2.代码实现
- 3.应用
本文参考:
LeetCode 208.实现 Trie (前缀树)
1.概述
前缀树又称字典树、Trie 树、单词查找树,是一棵有根树,同时也是一种哈希树的变种,其每个节点包含以下字段:
- 指向子节点的指针数组 children。一般来说,数组长度为 26,即小写英文字母的数量(也可根据实际情况自行设置)。此时 children[0] 对应小写字母 a,children[1] 对应小写字母 b,…,children[25] 对应小写字母 z。
- 布尔字段 isEnd,表示该节点是否为字符串的结尾。
2.代码实现
(1)Trie 树的代码实现如下:
class Trie {
private Trie[] children;
private boolean isEnd;
//构造函数
public Trie() {
//每个节点最多有 26 个子节点,分别对应 26 个小写英文字母
children = new Trie[26];
isEnd = false;
}
//插入字符串
public void insert(String word) {
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
//当前节点 node 为 word 的结尾节点
node.isEnd = true;
}
//搜索字符串
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
//判断前缀 prefix 是否存在于前缀树中
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
//返回前缀 prefix 的最后一个字符所在的节点
public Trie searchPrefix(String prefix) {
Trie node = this;
for (int i = 0; i < prefix.length(); i++) {
char ch = prefix.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/
(2)下面对代码中的一些细节进行说明:
- 插入字符串
从字典树的根开始,插入字符串。对于当前字符对应的子节点,有以下两种情况:
① 子节点存在。沿着指针移动到子节点,继续处理下一个字符。
② 子节点不存在。创建一个新的子节点,记录在 children 数组的对应位置上,然后沿着指针移动到子节点,继续搜索下一个字符。
重复以上步骤,直到处理字符串的最后一个字符,然后将当前节点标记为字符串的结尾,即令 isEnd = true。 - 查找前缀
从字典树的根开始,查找前缀。对于当前字符对应的子节点,有以下两种情况:
① 子节点存在。沿着指针移动到子节点,继续搜索下一个字符。
② 子节点不存在。说明字典树中不包含该前缀,返回空指针。
重复以上步骤,直到返回空指针或搜索完前缀的最后一个字符。若搜索到了前缀的末尾,就说明字典树中存在该前缀。此外,若前缀末尾对应节点的 isEnd 为 true,则说明字典树中存在该字符串。
(3)复杂度分析:
① 时间复杂度:初始化为 O(1),其余操作为 O(|S|),其中 |S| 是每次插入或查询的字符串的长度。
② 空间复杂度:O(∣T∣⋅Σ),其中 |T| 为所有插入字符串的长度之和,Σ 为字符集的大小,这里只存储所有的小写字母,即 Σ = 26。
(4)现在以下面的这些单词为例,来构造前缀树
{"good", "gold", "send", "sence", "word", "world"}
分析这些单词后可以发现:
① “good” 和 “gold” 的公共前缀为 “go”;
② “send” 和 “sence” 的公共前缀为 “sen”;
③ “word” 和 “world” 的公共前缀为 “wor”;
最终构造的前缀树如下图所示:
3.应用
(1)前缀树的典型应用是用于统计、排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
(2)大家可以去 LeetCode 上找相关的前缀树题目来练习,或者也可以直接查看 LeetCode算法刷题目录(Java)这篇文章中的前缀树章节。此外,如果大家发现文章中的错误之处,可在评论区中指出。