前缀树(字典树)
所以也叫 Trie树 – 字典树/单词查找树/键树 ,是一种树形结构,是一种哈希树的变种,典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用***字符串的公共前缀来减少查询时间***(常用于论文研究的最大原因),最大限度地减少无谓的字符串比较,查询效率比哈希树高。
前缀树可以高效地存储和检索字符串数据集中的键,这一个数据结构有相当多的应用场景,例如:自动补全和拼写检查
前缀树的三个基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符
- 从根节点到某一节点,路径上经过的字符连接起来,为该结点对应的字符串
- 每个节点的所有子节点包含的字符都不相同
前缀树的应用
- 串的快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。 - “串”排序
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出。
用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。 - 最长公共前缀 (***)
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。(在DP差分隐私论文中用的会比较多)
注意:前缀树中查询次数和样本数量无关,跟要查询的字符串长度有关
例子:单词查询 - python
- 对于单词查询例子来说,使用字典前缀树会比较好一点,前提是空间大小允许,因为字典树的空间相比较hash还是比较浪费的,毕竟hash可以使用bit数组。
那么在那么在空间要求不那么严格的情况下,字典树的效率不一定比hash弱,它支持动态查询,比如apple,当用户输入到appl时,字典树此刻的查询位置可以就到达l这个位置,那么我在输入e时光查询e就可以了(更何况如果我们直接用字母的ASCII作下标肯定会更快)!字典树它并不用等待你完全输入完毕后才查询。
class Trie:
# 初始化
def __init__(self):
self.root = {}
self.end = "#"
# 添加单词
def add(self, word: str):
node = self.root
for char in word:
node = node.setdefault(char, {})
node[self.end] = None
# 搜索单词是否存在
def search(self, word):
node = self.root
for char in word:
if char not in node:
return False
node = node[char]
return self.end in node
对于前缀树举一个详细的例子:
实例:前缀树的创建
好比假设有b,abc,abd,bcd,abcd,efg,hii 这6个单词,我们进行创建前缀树就可以得到:
对于单词查询的demo实现
1、插入一个新单词
// 插入一个新单词
public static void insert(TrieNode root,String str){
if(root==null||str.length()==0){
return;
}
char[] c=str.toCharArray();
for(int i=0;i<str.length();i++){
//如果该分支不存在,创建一个新节点
if(root.nextNode[c[i]-'a']==null){
root.nextNode[c[i]-'a']=new TrieNode();
}
root=root.nextNode[c[i]-'a'];
root.prefix++;//注意,应该加在后面
}
//以该节点结尾的单词数+1
root.count++;
}
2、查询以str开头的单词数量,查询单词str的数量
// 查找该单词是否存在,如果存在返回数量,不存在返回-1
public static int search(TrieNode root,String str){
if(root==null||str.length()==0){
return -1;
}
char[] c=str.toCharArray();
for(int i=0;i<str.length();i++){
//如果该分支不存在,表名该单词不存在
if(root.nextNode[c[i]-'a']==null){
return -1;
}
//如果存在,则继续向下遍历
root=root.nextNode[c[i]-'a'];
}
//如果count==0,也说明该单词不存在
if(root.count==0){
return -1;
}
return root.count;
}
3、查询以str为前缀的单词数量
// 查询以str为前缀的单词数量
public static int searchPrefix(TrieNode root,String str){
if(root==null||str.length()==0){
return -1;
}
char[] c=str.toCharArray();
for(int i=0;i<str.length();i++){
//如果该分支不存在,表名该单词不存在
if(root.nextNode[c[i]-'a']==null){
return -1;
}
//如果存在,则继续向下遍历
root=root.nextNode[c[i]-'a'];
}
return root.prefix;
}
**plus:**想要对前缀树更加了解可以在刷一道算法题,
力扣208.实现Trie(前缀树)
题目描述:
示例:
提示: