一、Trie树
Trie树又叫字典树、前缀树、单次查找树,是一种多叉树结构,如下图所示
- Trie树的基本性质
1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
2.从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
3.每个节点的所有子节点包含的字符互不相同。 - Trie树的优点
1.插入和查询的效率很高,都为O(m),其中m是待插入/查询的字符串的长度。
2.Trie树中不同的关键字不会产生冲突
3.Trie树只有再允许一个关键字关联多个值的情况下才有类似hash碰撞产生
4.Tire树相比hash表不用求hash值,对短字符串有更快的速度。因为,通常求hash值也是需要遍历字符串的
5.Trie树可以对关键字按字典序排序
Trie树的核心思想是空间换时间,利用字符的公关前缀来减少无谓的字符串比较以达到提高查询效率的目的。 - Trie树的缺点
1.当hash函数较好时,Trie树的查找效率会低于哈希查找
2.空间消耗较大
二、Trie树的应用
- 字符串检索
- 词频统计
- 字符串排序
- 前缀匹配
三、Trie树结构与实现
- Trie树单个节点结构
一般Trie树用于单词的查询,所以每个节点用来代表某个字母。Trie树是一个多路查找树,所以需要建立与下一个节点的关系。所以用一个节点数组来定义每一个节点,实现嵌套连接关系,此外该数组的长度为26,可以用数组的下标index来映射该节点代表的字母。如下图所示,一般还会加一个标记位isEnd,来标记该节点是否为末尾节点。
-
前缀树结构
以copy和cool两个单词为例
-
代码实现(java版本)
class Trie {
// 字典树/前缀树
// 用一个数组维护一个Trie节点
// isEnd标记该节点是否为前缀树的末位
private Trie[] childen;
private boolean isEnd;
public Trie() {
this.childen = new Trie[26];
isEnd = false;
}
public void insert(String word) {
// 找到当前节点下标所在位置,如果为空则新建,否则继续遍历
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
int index = c - 'a';
if (node.childen[index] == null) {
node.childen[index] = new Trie();
}
node = node.childen[index];
}
node.isEnd = true;
}
public boolean search(String word) {
// 统一写进search前缀方法
Trie node = searchPrefix(word);
// 需要判断当前节点是否是末位,中间出现子串不能算查询到了
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for (int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
int index = c - 'a';
if (node.childen[index] == null) {
return null;
}
node = node.childen[index];
}
return node;
}
}