前言
力扣中有很多道题目可以用前缀树来解决。作为一种树的数据结构,前缀树被广泛应用在很多场景中,比如在搜索引擎中输入几个字,就会有自动补全的一系列选项出来。
本文将讲两道题,一道是前缀树的实现,另一道题是前缀树的应用。 题目链接: leetcode-cn.com/problems/im…
题目:实现前缀树
代码
class Node:
def __init__(self):
self.child = [None for _ in range(26)]
self.isEnd = False
def containKey(self, ch):
return self.child[ord(ch) - ord('a')]
def get(self, ch):
return self.child[ord(ch) - ord('a')]
def put(self, ch):
self.child[ord(ch) - ord('a')] = Node()
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = Node()
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
p = self.root
n = len(word)
for i in range(n):
if not p.containKey(word[i]):
p.put(word[i])
p = p.get(word[i])
p.isEnd = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
p = self.root
for i in word:
if not p.containKey(i):
return False
else:
p = p.get(i)
return p.isEnd
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
p = self.root
for i in prefix:
if not p.containKey(i):
return False
else:
p = p.get(i)
return True
解读
第一步,就是实现一个新的类叫做Node,表示这棵树上的结点。有两个属性:child即是这个结点的子节点,用长度为26的列表来表示(有26个字母);isEnd表示是否为叶子结点。
插入一个词:遍历这个词。如果这个词有在树上,就不必添加结点;如果这个词不在树上,就添加结点。遍历结束后需要把最后一个结点的isEnd属性设置为True.
查找一个词:从树的根开始找,如果搜索不到结点,就返回False。遍历完这个词,就返回isEnd。如果isEnd是True,表明这个词存在;如果isEnd是False,表明这个词不在这棵树上。
判断是否有以“prefix”开头的词:和查找一个词的算法类似,但是遍历完这个词就直接返回True,不需要判断当前结点是否为叶子结点。
题目:前缀树的应用
解读
看到插入单词,查找单词就很容易联想到前缀树。然而这道题目的难点在于如何处理"."。出现了一个".",就意味着在这个位置,有多种选择,那么我们可以选择用深度优先搜索来判断这个单词是否存在。
我们可以用比较简单的方式来实现前缀树:用字典!每一个key都表示一个结点。
代码
class WordDictionary:
def __init__(self):
"""
Initialize your data structure here.
"""
self.dic = {}
def addWord(self, word: str) -> None:
curr = self.dic
for w in word:
if w in curr:
curr = curr[w]
else:
curr[w] = {}
curr = curr[w]
curr['end'] = True
def search(self, word: str) -> bool:
return self.dfs(word, self.dic, False, [])
def dfs(self, word, node, cut, ls):
if cut:
return True
curr = node
for i, c in enumerate(word):
if c == '.':
for k, v in curr.items():
if k != 'end':
ls.append(self.dfs(word[i+1:], curr[k], cut, ls))
return any(ls)
if c not in curr:
return False
curr = curr[c]
cut = 'end' in curr
return cut
总结
弄明白字典树的实现方式可以让我们解决一系列问题。重点理解一下插入和查找过程,相信我们都可以做到举一反三!