LeetCode 第 208 题:实现 Trie (前缀树)
字数 1522 2025-10-26 08:43:37
好的,我们来看 LeetCode 第 208 题:实现 Trie (前缀树)。
题目描述
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()初始化前缀树对象。void insert(String word)向前缀树中插入字符串word。boolean search(String word)如果字符串word在前缀树中,返回true(即,在插入之后已经存在);否则,返回false。boolean startsWith(String prefix)如果之前已经插入的字符串word的前缀之一为prefix,返回true;否则,返回false。
解题思路
1. 理解 Trie 的结构
Trie 是一棵多叉树,每个节点包含:
- 一个指向子节点的指针数组(长度 26,对应 26 个小写英文字母)。
- 一个布尔值
isEnd,表示该节点是否是某个单词的结束节点(即从根节点到该节点的路径构成了一个完整的单词)。
例如,插入 "apple" 后,Trie 的结构如下(* 表示 isEnd = true):
根节点
-> a -> p -> p -> l -> e*
再插入 "app":
根节点
-> a -> p -> p* -> l -> e*
app 的最后一个 p 的 isEnd 为 true,表示 "app" 也是一个完整单词。
2. 设计 Trie 节点类
我们可以先定义节点结构:
children: 大小为 26 的数组,初始为null。isEnd: 标记单词结束。
3. 插入操作
从根节点开始,对于要插入的字符串 word 的每个字符:
- 计算字符
ch在children中的索引:index = ch - 'a'。 - 如果当前节点的
children[index]为null,则新建一个节点并指向它。 - 移动到子节点,继续处理下一个字符。
- 处理完最后一个字符后,将当前节点的
isEnd设为true。
例子:插入 "and":
- 根节点:
children['a'-'a']新建节点 A。 - 节点 A:
children['n'-'a']新建节点 N。 - 节点 N:
children['d'-'a']新建节点 D。 - 节点 D:
isEnd = true。
4. 搜索操作
从根节点开始,对于 word 的每个字符:
- 计算索引
index = ch - 'a'。 - 如果
children[index]为null,说明 Trie 中不存在该单词,返回false。 - 否则移动到子节点。
- 处理完所有字符后,检查当前节点的
isEnd是否为true(确保是完整单词,而不是前缀)。
5. 搜索前缀操作
与 search 类似,但不需要检查 isEnd,只要路径上的每个字符都存在,就返回 true。
代码实现(Java)
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
Trie node = this;
for (char ch : word.toCharArray()) {
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for (char ch : prefix.toCharArray()) {
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
复杂度分析
- 时间复杂度:插入、搜索、搜索前缀的时间复杂度均为 O(m),其中
m为字符串长度。 - 空间复杂度:最坏情况下需要存储所有字符,空间复杂度为 O(26 * m * n),其中
n为键的个数,m为平均键长。
总结
Trie 的核心思想是用公共前缀来减少查询时间,常用于处理字符串的快速检索、前缀匹配等场景。掌握 Trie 的基本操作后,可以进一步解决更复杂的问题,如添加通配符、压缩 Trie 等。