一、什么是Trie树
Trie树,也称为前缀树,是一种高效的字符串存储、查找数据结构。它的核心思想是把字符串分成一个一个单元,用树形结构进行存储。
这种数据结构的最大特点是可以快速地进行前缀匹配,所以常用于大规模字符串的查找、分类、排序等操作。
Trie树的名字来源于retrieval,意为“检索”、“找回”。Trie树的最初设计是由Edward Fredkin在1960年代提出的。
二、Trie树的基本概念
对于每个字符串,我们可以把它看成一个由若干个字符组成的序列,例如"hello"可以看成"{'h', 'e', 'l', 'l', 'o'}"。
在Trie树中,每个节点都对应一段字符串,节点的子节点代表了可能的下一步字符。对于每个节点,它的所有子节点代表的字符集合是互不重叠的,也就是说,从根节点到某个节点的路径表示的字符串必须是唯一的。
三、如何构建Trie树
首先我们需要在根节点处建立一个空的Trie树。然后,我们依次把所有的字符串插入到Trie树中。
插入一个字符串的方法如下:
- 从根节点开始,看第一个字符是否已经存在于Trie树中。
- 如果存在,就跳到这个字符对应的子节点,继续判断下一个字符。
- 如果不存在,则新建一个节点作为当前节点的子节点,表示添加一个新的字符。
- 按照上述方法,一直添加到字符串的最后一个字符为止。
- 在字符串的最后一个节点打上一个标志,表示这是一个字符串的结尾。
下面是Trie树基本代码实现:
class TrieNode(object): def __init__(self): self.children = {} self.is_end = False class Trie(object): def __init__(self): self.root = TrieNode() def insert(self, word): node = self.root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.is_end = True def search(self, word): node = self.root for char in word: if char not in node.children: return False node = node.children[char] return node.is_end def starts_with(self, prefix): node = self.root for char in prefix: if char not in node.children: return False node = node.children[char] return True
四、Trie树的应用
1. 自动补全
利用Trie树可以实现字符自动补全功能。具体实现方法是,从Trie树的根节点开始,根据用户输入的前缀搜索Trie树,找到以该前缀为前缀的字符串,将其推荐给用户。
Trie树可以实现快速的字符串的前缀查找,因此它比较适合用于实现自动补全功能。下面是基于Trie树的自动补全代码实现:
class TrieNode(object): def __init__(self): self.children = {} self.is_end = False self.word = None class Trie(object): def __init__(self): self.root = TrieNode() def insert(self, word): node = self.root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.is_end = True node.word = word def search(self, word): node = self.root for char in word: if char not in node.children: return False node = node.children[char] return node.is_end def starts_with(self, prefix): node = self.root for char in prefix: if char not in node.children: return None node = node.children[char] return node def dfs(self, node, words): if node: if node.is_end: words.append(node.word) for child in node.children.values(): self.dfs(child, words) def query(self, x): node = self.starts_with(x) words = [] self.dfs(node, words) return words
2. 字符串排序
利用Trie树可以实现字符串的快速排序功能。由于Trie树存储的是整个字符串集合,因此,对Trie树进行前序遍历,输出每一个字符串即可得到一个有序字符串序列。即对Trie树的DFS序遍历结果进行排序。
下面是基于Trie树实现字符串排序代码:
class TrieNode(object): def __init__(self): self.children = {} self.is_end = False class Trie(object): def __init__(self): self.root = TrieNode() def insert(self, word): node = self.root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.is_end = True def dfs(self, node, word, res): if node.is_end: res.append(word) for char in node.children: self.dfs(node.children[char], word+char, res) def sort_words(self, words): trie = Trie() for word in words: trie.insert(word) res = [] trie.dfs(trie.root, "", res) return res
3. 统计词频
由于Trie树的每个节点代表一个字符串,因此可以通过记录每个节点是否为字符串结尾来统计字符串出现的次数。具体实现方法是,在Trie树的每个节点处设置一个计数器freq,每次遇到一个字符串结尾节点,就将对应的freq计数器加1。
下面是基于Trie树实现统计词频的代码:
class TrieNode(object): def __init__(self): self.children = {} self.is_end = False self.freq = 0 class Trie(object): def __init__(self): self.root = TrieNode() def insert(self, word): node = self.root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.is_end = True node.freq += 1 def search(self, word): node = self.root for char in word: if char not in node.children: return False node = node.children[char] return node.is_end def starts_with(self, prefix): node = self.root for char in prefix: if char not in node.children: return None node = node.children[char] return node def count_words(self, word): node = self.starts_with(word) if not node: return 0 return node.freq
五、Trie树的时间复杂度
Trie树的时间复杂度与插入字符串的长度有关。将一个长度为L的字符串插入到Trie树中,需要O(L)的时间。对于在Trie树中搜索一个字符串或者进行前缀搜索,时间复杂度也与字符串长度有关,也是O(L)。
因此,Trie树在处理字符串存储和搜索方面非常高效。
六、总结
本文从Trie树的基本概念、构建方法、应用场景以及时间复杂度方面进行了详细的讲解,并且给出了用Python实现的Trie树代码。除此之外,Trie树还可以用于词频统计、字符串去重、最长公共前缀等问题。
有了Trie树这种高效的数据结构,我们就可以更加方便地处理大规模的字符串问题。