字典树(Trie)是什么?
字典树,也叫前缀树,是一种用来存储字符串集合的数据结构,特别适合快速查找、插入和前缀匹配。
形象比喻:图书馆里的“字母书架”
想象你走进一个超级大的图书馆,里面有成千上万本书,每本书的名字都是一个单词。现在,图书馆馆长想设计一个超级智能书架,让你能快速找到所有以某个字母开头,或者某个前缀开头的书。
1. 书架的设计
- 书架不是普通的直排,而是分层的。
- 第一层是所有单词的第一个字母,比如有书名以“A”、“B”、“C”开头,就有对应的分区。
- 第二层是第二个字母的分区,依次类推。
2. 你找书的过程
假设你想找所有以“cat”开头的书:
- 你先去第一层的“c”区。
- 再去“c”区里的“a”区。
- 再去“a”区里的“t”区。
- 这个“t”区下的所有书,都是以“cat”开头的。
3. Trie树的结构
- 每个节点代表一个字母。
- 从根节点到某个节点的路径,拼起来就是一个字符串的前缀。
- 叶子节点或者标记节点表示一个完整的单词。
4. 举个例子
假设有单词集合:["cat", "car", "dog", "door"]
Trie树结构大致是:
(root)
├─ c
│ ├─ a
│ ├─ t (单词结束)
│ └─ r (单词结束)
└─ d
├─ o
├─ g (单词结束)
└─ o
└─ r (单词结束)
5. 优点
- 查找单词或前缀非常快,时间复杂度与单词长度相关。
- 节省存储空间,因为公共前缀共享节点。
总结比喻
Trie树元素 | 图书馆比喻 |
---|---|
节点 | 书架上的字母分区 |
路径 | 书名的字母顺序 |
单词结束标记 | 书架上标记“这儿有完整书” |
我们继续,给你讲讲字典树(Trie)的基本操作和简单代码示例,帮助你更好理解。
字典树的基本操作
-
插入单词(Insert)
把一个单词的每个字母依次放到树上,如果节点不存在就新建,最后标记单词结束。
-
查找单词(Search)
按字母顺序从根节点往下找,如果能找到对应路径且最后节点是单词结束,说明单词存在。
-
查找前缀(StartsWith)
按字母顺序从根节点往下找,如果能找到对应路径,说明有单词以这个前缀开头。
简单代码示例(Python)
class TrieNode:
def __init__(self):
self.children = {} # 存储子节点,key是字母,value是TrieNode
self.is_end = False # 是否是单词结尾
class Trie:
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 startsWith(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
# 使用示例
trie = Trie()
trie.insert("apple")
print(trie.search("apple")) # True
print(trie.search("app")) # False
print(trie.startsWith("app")) # True
trie.insert("app")
print(trie.search("app")) # True
总结
- 字典树用节点存储字母,节点之间连接形成单词路径。
- 插入时逐字母创建节点。
- 查找时逐字母遍历节点。
- 可以快速判断单词是否存在,或者是否有某个前缀。
我们继续深入讲解字典树(Trie),这次介绍一些常见应用和优化思路,帮助你更全面理解。
字典树的常见应用
-
自动补全(Autocomplete)
输入一个前缀,快速找到所有以该前缀开头的单词,比如手机输入法、搜索引擎。
-
拼写检查(Spell Checker)
判断一个单词是否存在,或者找出拼写相近的单词。
-
单词统计
统计某个前缀出现的次数,或者统计所有单词。
-
最长公共前缀
多个单词的最长公共前缀可以通过Trie树快速找到。
-
字符串匹配
在大量字符串中快速查找某个模式。
Trie的优化思路
-
使用数组代替字典
如果字符集固定(比如只包含小写字母a-z),可以用长度为26的数组代替字典,节省空间和加快访问。
-
压缩路径(压缩Trie)
把只有一个子节点的路径合并成一个节点,减少树的高度,节省空间。
-
存储额外信息
比如每个节点存储经过该节点的单词数量,方便统计。
代码示例:用数组实现Trie节点(只支持小写字母)
class TrieNode:
def __init__(self):
self.children = [None] * 26 # 26个字母
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def _char_to_index(self, ch):
return ord(ch) - ord('a')
def insert(self, word):
node = self.root
for ch in word:
idx = self._char_to_index(ch)
if not node.children[idx]:
node.children[idx] = TrieNode()
node = node.children[idx]
node.is_end = True
def search(self, word):
node = self.root
for ch in word:
idx = self._char_to_index(ch)
if not node.children[idx]:
return False
node = node.children[idx]
return node.is_end
def startsWith(self, prefix):
node = self.root
for ch in prefix:
idx = self._char_to_index(ch)
if not node.children[idx]:
return False
node = node.children[idx]
return True
总结
- Trie不仅能快速查找单词,还能高效处理前缀相关问题。
- 通过优化数据结构和存储方式,可以提升性能和节省空间。
- Trie广泛应用于搜索引擎、输入法、拼写检查等领域。