字典树
前缀树是一种树形结构数据,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用场景,例如自动补完和拼写检查。
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
提示
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
,search
和startsWith
调用次数总计不超过 3 × 1 0 4 3\times10^4 3×104次
例子
Input:
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output:
[None, None, True, False, True, None, True]
1. 初始化
- 指向节点的指针数组
- 布尔字段
isEnd
def __init__(self):
# 算是26叉树吧, 每个元素的对象都可以是Trie
self.children = [None] * 26
self.isEnd = False
2. 插入字符串
- 子节点存在,处理下一个字符
- 子节点不存在,创建一个新的子节点,记录在
children
数组的对应位置上
def insert(self, word):
node = self
for ch in word:
ch = ord(ch) - ord("a")
# 不存在就创建
if not node.children[ch]:
node.children[ch] = Trie()
node = node.children[ch]
# 标记一下结尾
node.isEnd = True
3. 查找前缀
- 子节点存在
- 子节点不存在,说明字典树不包含该前缀,返回空指针
def search(self, word):
node = self
for ch in word:
ch = ord(ch) - ord("a")
# 不存在就返回False
if not node.children[ch]:
return False
node = node.children[ch]
# isEnd = True 才算真的有
return node is not None and node.isEnd
4. 查找单词
和查找前缀一样的操作
def startsWith(self, prefix):
node = self
for ch in prefix:
ch = ord(ch) - ord("a")
# 不存在就返回False
if not node.children[ch]:
return False
node = node.children[ch]
# prefix不需要isEnd加持
return node is not None
class Trie(object):
def __init__(self):
self.children = [None] * 26
self.isEnd = False
def insert(self, word):
node = self
for ch in word:
ch = ord(ch) - ord("a")
if not node.children[ch]:
node.children[ch] = Trie()
node = node.children[ch]
node.isEnd = True
def search(self, word):
node = self
for ch in word:
ch = ord(ch) - ord("a")
if not node.children[ch]:
return False
node = node.children[ch]
return node is not None and node.isEnd
def startsWith(self, prefix):
node = self
for ch in prefix:
ch = ord(ch) - ord("a")
if not node.children[ch]:
return False
node = node.children[ch]
return node is not None
复杂度
- 时间复杂度:初始化是 O ( 1 ) O(1) O(1),其余为 O ( ∣ S ∣ ) O(|S|) O(∣S∣), ∣ S ∣ |S| ∣S∣是每次插入字符串的长度
- 空间复杂度: O ( ∣ T ∣ ⋅ Σ ) O(|T|\cdot\Sigma) O(∣T∣⋅Σ), ∣ T ∣ |T| ∣T∣为字符串长度之和, Σ \Sigma Σ为字符集大小
字典树+DFS
class Trie:
def __init__(self):
self.children = [None] * 26
self.isEnd = False
def insert(self, word):
node = self
for ch in word:
ch = ord(ch) - 97
if not node.children[ch]:
node.children[ch] = Trie()
node = node.children[ch]
node.isEnd = True
def myFun(self, d):
self.ans = 0
self.dfs(d, 0, self)
return self.ans
def dfs(self, d, start, node):
if start == len(d):
if node.isEnd == True:
self.ans += 1
return
else:
return
for ch in d[start]:
ch = ord(ch) - 97
if node.children[ch] == None:
continue
temp = node
node = node.children[ch]
self.dfs(d, start + 1, node)
node = temp
class Encrypter(object):
def __init__(self, keys, values, dictionary):
self.keyToValue = defaultdict(str)
self.valueToKey = defaultdict(list)
for i in range(len(keys)):
self.keyToValue[keys[i]] = values[i]
self.valueToKey[values[i]].append(keys[i])
self.root = Trie()
for word in dictionary:
self.root.insert(word)
def encrypt(self, word1):
s = ''
for ch in word1:
s += self.keyToValue[ch]
return s
def decrypt(self, word2):
d = []
for i in range(0, len(word2), 2):
d.append(self.valueToKey[word2[i:i + 2]])
return self.root.myFun(d)
# Your Encrypter object will be instantiated and called as such:
# obj = Encrypter(keys, values, dictionary)
# param_1 = obj.encrypt(word1)
# param_2 = obj.decrypt(word2)
注意事项:回溯完后要回到该字典树节点的父节点