字典树(Trie)
0 字典树的引入
我们已经知道树和二叉搜索树等数据结构,那么字典树或者前缀树是用来做什么的呢?
假设我们需要创建一个能提供单词拼写检查的程序,它能给出这个单词是否有效(不需要给出建议的单词)。那么我们该如何实现呢?
最简单的方式是利用一个具有所有已知词汇的哈希表,该哈希表能在O(1)
的时间内判断单词是否存在,但是它需要的存储将会是O(n*m)
,n
是单词的个数,m
是单词的长度。如果利用字典树(前缀树),那么将能够降低空间复杂度只牺牲一点时间性能。
1 基础的字典树
首先来看有着"a","add"和"hi"的基础字典树:
利用字典实现: 1
2
3
4
5
6
7
8
9
10
11
12basic_trie = {
# a 和 add
'a':{
'd':{
'd':{'word_end': True},
'word_end': False},
'word_end': True},
# hi
'h':{
'i':{'word_end': True},
'word_end': False}
}word_end
是否为True
来查找词汇。在basic_trie
中,a
与add
有重叠(拥有共同的前缀),这就是字典树节省存储空间的地方(不用再单独为a
和add
开辟空间)。下面的is_word
函数便是用于检查一个词汇是否存在于basic_trie
中:
1 | def is_word(word): |
测试is_word
: 1
2
3
4
5
6test_words = ['ap', 'add']
for word in test_words:
if is_word(word):
print('"{}" is a word.'.format(word))
else:
print('"{}" is not a word.'.format(word))
2 字典树
从上面的简单的字典树中可以看出,字典树是一棵多叉树,根节点不包含字符,从根节点到某节点的路径上的字符连起来就是该节点对应的字符串。
实现代表字典树的类
字典树的类中包含一个存储字符的字典,还有add
和exist
方法分别用于增加一个词汇、判断词是否存在。
1 | class TrieNode(object): |
测试上面的Trie
类: 1
2
3
4
5
6
7
8
9
10
11
12
13
14word_list = ['apple', 'bear', 'goo', 'good', 'goodbye', 'goods', 'goodwill', 'gooses' ,'zebra']
word_trie = Trie()
# Add words
for word in word_list:
word_trie.add(word)
# Test words
test_words = ['bear', 'goo', 'good', 'goos']
for word in test_words:
if word_trie.exists(word):
print('"{}" is a word.'.format(word))
else:
print('"{}" is not a word.'.format(word))
"goo" is a word.
"good" is a word.
"goos" is not a word.
3 字典树查找的时间复杂度
字典树查找的时间复杂度是O(h)
,h
是单词的长度。
4 完成leetcode上的《实现 Trie (前缀树)
题目链接在这里
4.1 题目描述
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
1 | Trie trie = new Trie(); |
说明:
你可以假设所有的输入都是由小写字母 a-z 构成的。 保证所有输入均为非空字符串。
4.2 python代码
1 | class TrieNode: |