问题描述:
思路简述:
借用一张图
前缀树又叫做单词查找树,是一种树形数据结构,可以用于存储大量的字符串,优点是可以利用不同字符串的公共前缀来节省大量的存储空间,避免了顺序存储字符串列表的冗余,可以看成是一个二十六叉树,一个子节点对应的就是二十六个字母中的一个,下面给出前缀树中一个节点的数据结构(以Python为例):
class TrieNode:
def __init__(self):
self.isWord = True # 是否是单词
self.children = [0] * 26
每个节点维护两个信息,一个是子节点的一个列表,一个是标识当前节点构成的字符串是否是存储的字符串,下面用一个实际例子来说明:
1、构造前缀树:
假设当前前缀树为空,即只有一个根节点,现在要向前缀树中插入字符串,比如"chen"字符串,则对该字符串按单字符进行遍历,因为当前前缀树为空,所以四个节点应该会被创建,得到的结果即:
除了最后一个“N”节点中isWord = True(表示“N”节点及之前的所有节点构成的字符为一个存储的字符串),其他节点的该属性都应该为False(表示并没有构成字符串)。
2、插入字符串
现在得到了存储了"chen"字符串的前缀树,如果要继续插入另一个字符串"chan",可以看到这个字符串与之前已经存储了的"chen"有相同的前缀"ch",在插入时也是先按字符对“chan”字符串遍历,第一个字符'c'发现在根节点的children中存在这个子节点,于是直接复用这个字符,再继续发现字符'h'也是存在于'c'节点的子节点中的,于是又复用这个'h'节点,直到判断'h'节点的子节点,没有发现'a'节点,这时候才会去新建节点,插入完成后得到的前缀树如图:
3、查找字符串
在前缀树中查找字符串是否存在也很简单,比如要查找"chan"是否存在,先从根节点开始,先判断根节点的children中'c'是否存在,如果存在则判断'c'节点的children中'h'是否存在 。。。 。。。
整个过程中只要有一个字符不存在,则可以直接认为前缀树中不存在该字符串,如果所有节点都存在,还应该判断最终的节点的isWord是否为True,如果为False也应当认为前缀树中不存在该字符串,因为如果前缀树中存储的"helloworld"字符串,查找的"hello",则一定也能够查找到,但这时候'o'节点的isWord是为False的,所以也不能认为它存储了我们要查找的字符串。
有了这样的前置知识这道题就非常简单了。
代码实现:
class TrieNode:
def __init__(self):
self.isWord = True # 是否是单词
self.children = [0] * 26
class Trie:
def __init__(self):
self.root = TrieNode()
def find(self, word:str) -> TrieNode:
current = self.root
for w in word:
index = ord(w) - ord('a')
if current.children[index] == 0:
return None
current = current.children[index]
return current
def insert(self, word: str) -> None:
current = self.root
for w in word:
index = ord(w) - ord('a')
if current.children[index] == 0:
current.children[index] = TrieNode()
current.children[index].isWord = False
current = current.children[index]
current.isWord = True
def search(self, word: str) -> bool:
current = self.find(word)
return current!=None and current.isWord
def startsWith(self, prefix: str) -> bool:
return self.find(prefix)!=None
注:在search和startswith方法中,都需要对前缀树查找,可以抽取一个公共方法find()