【Leetcode hot 100】208.实现Trie(前缀树)
问题链接
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
。
示例:
输入
[“Trie”, “insert”, “search”, “search”, “startsWith”, “insert”, “search”]
[[], [“apple”], [“apple”], [“app”], [“app”], [“app”], [“app”]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert(“apple”);
trie.search(“apple”); // 返回 True
trie.search(“app”); // 返回 False
trie.startsWith(“app”); // 返回 True
trie.insert(“app”);
trie.search(“app”); // 返回 True
提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 10^4
次
问题解答
1. 前缀树(Trie)核心原理
前缀树是一种专门用于高效存储和检索字符串的树形数据结构,其核心特点是:
- 节点不存储完整字符:每个节点代表字符串的一个字符片段,通过节点在树中的路径拼接出完整字符串。
- 子节点数组优化:因题目限定仅含小写英文字母,每个节点用大小为
26
的数组存储子节点(对应a-z
),索引通过字符 - 'a'
计算。 - 单词结尾标记:每个节点用布尔值
isEndOfWord
标记是否为某个单词的结尾,避免混淆“前缀”和“完整单词”(如区分app
和apple
)。
2. 完整Java代码实现
class Trie {// 前缀树节点类(静态内部类,仅服务于Trie)private static class TrieNode {// 存储子节点:索引 0-25 对应 a-zTrieNode[] children;// 标记当前节点是否为某个单词的结尾boolean isEndOfWord;// 节点构造器:初始化子节点数组(默认全为null),标记为非单词结尾public TrieNode() {children = new TrieNode[26]; // 仅小写英文字母,大小固定26isEndOfWord = false;}}// 前缀树的根节点(空节点,不对应任何字符)private TrieNode root;// 初始化前缀树(根节点为空)public Trie() {root = new TrieNode();}/*** 向前缀树插入单词* @param word 待插入的字符串(仅小写英文字母)*/public void insert(String word) {// 从根节点开始遍历TrieNode currentNode = root;for (int i = 0; i < word.length(); i++) {char c = word.charAt(i);// 计算当前字符对应的子节点索引(a->0, b->1, ..., z->25)int index = c - 'a';// 若当前字符的子节点不存在,创建新节点if (currentNode.children[index] == null) {currentNode.children[index] = new TrieNode();}// 移动到子节点,继续处理下一个字符currentNode = currentNode.children[index];}// 遍历完所有字符后,标记当前节点为单词结尾currentNode.isEndOfWord = true;}/*** 搜索单词是否在前缀树中(需是完整单词)* @param word 待搜索的字符串* @return 存在则返回true,否则返回false*/public boolean search(String word) {// 从根节点开始遍历,找到单词最后一个字符对应的节点TrieNode currentNode = root;for (int i = 0; i < word.length(); i++) {char c = word.charAt(i);int index = c - 'a';// 若中途某个字符的子节点不存在,说明单词不存在if (currentNode.children[index] == null) {return false;}currentNode = currentNode.children[index];}// 遍历完所有字符后,需确认当前节点是单词结尾(避免仅为前缀的情况)return currentNode.isEndOfWord;}/*** 判断前缀树中是否存在以指定前缀开头的单词* @param prefix 待判断的前缀* @return 存在则返回true,否则返回false*/public boolean startsWith(String prefix) {// 逻辑与search类似,但无需判断是否为单词结尾TrieNode currentNode = root;for (int i = 0; i < prefix.length(); i++) {char c = prefix.charAt(i);int index = c - 'a';if (currentNode.children[index] == null) {return false;}currentNode = currentNode.children[index];}// 遍历完所有前缀字符,说明前缀存在return true;}// 测试示例(与题目示例一致)public static void main(String[] args) {Trie trie = new Trie();trie.insert("apple");System.out.println(trie.search("apple")); // 输出:trueSystem.out.println(trie.search("app")); // 输出:falseSystem.out.println(trie.startsWith("app")); // 输出:truetrie.insert("app");System.out.println(trie.search("app")); // 输出:true}
}
3. 关键方法解析
TrieNode 节点类
- children 数组:大小固定为
26
,对应a-z
26个小写英文字母,避免哈希表的额外开销,访问效率为O(1)
。 - isEndOfWord 标记:例如插入
apple
后,e
对应的节点isEndOfWord = true
,而p
(第三个字符)对应的节点为false
,因此search("app")
会返回false
。
insert 插入方法
- 从根节点开始,逐个处理单词的每个字符。
- 对每个字符,计算其在
children
数组中的索引(c - 'a'
)。 - 若对应索引的子节点为空,创建新节点;否则直接复用已有节点。
- 处理完所有字符后,将最后一个节点的
isEndOfWord
设为true
,标记单词结束。
search 搜索方法
- 流程与
insert
一致,逐个匹配字符。 - 若中途某字符的子节点不存在,直接返回
false
(单词不存在)。 - 匹配完所有字符后,必须检查当前节点的
isEndOfWord
:仅当为true
时,才说明是完整单词(而非前缀)。
startsWith 前缀判断方法
- 流程与
search
一致,逐个匹配前缀字符。 - 若中途某字符的子节点不存在,返回
false
。 - 匹配完所有前缀字符后,直接返回
true
(无需判断isEndOfWord
,因为只需确认前缀存在)。
4. 复杂度分析
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
insert | O(L) | O(L) |
search | O(L) | O(1) |
startsWith | O(L) | O(1) |
- L:输入字符串(单词或前缀)的长度。
- 时间复杂度:每个方法均需遍历字符串的所有字符,故为
O(L)
,与插入的单词总数无关。 - 空间复杂度:
insert
需创建新节点,空间开销与字符串长度L
成正比;search
和startsWith
仅遍历节点,无额外空间开销。
5. 应用场景
前缀树的高效性使其在以下场景中广泛应用:
- 搜索引擎自动补全(如输入“app”提示“apple”“application”)。
- 拼写检查器(快速判断单词是否存在)。
- 词频统计(在节点中增加
count
字段统计单词出现次数)。 - IP 路由最长前缀匹配(网络领域的经典应用)。