实现 Trie (前缀树)
Implement Trie (Prefix Tree)
实现 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 <= 2000word和prefix仅由小写英文字母组成insert、search和startsWith调用次数 总计 不超过3 * 104次
解题思路
主解法
26 叉树 · 路径即前缀
每个节点代表一个字符,end 标记单词结尾
Trie 把字符串按字符拆成树:根到某个节点的路径就是一个前缀。插入时沿字符走,没有节点就新建;走完整词后,在最后一个节点打上 end。
分步图解
插入 "map":沿 m → a → … 创建缺失节点,最后一个节点标记 end。
- word
- map
Trie · 每个节点代表一个字符前缀
三个操作的区别只在终点判断:
insert:建路径,终点end = true。search:路径必须存在,并且终点带 end。startsWith:只要求路径存在,不关心终点是否是单词。
图解
普通节点当前操作已确认 / 结果排除 / 冲突
自拟词表共享前缀 m → a,然后分成 p、t、x 三条路;math 和 mate 又在 t 后分叉。查询 mat 时路径存在但没有 end,所以 search 返回 false,而 startsWith("ma") 返回 true。
每个操作只走单词长度 L 步,时间 O(L);空间与所有插入单词的总字符数成正比。
时间 O(L)空间 O(总字符数)
易错点
- search 只检查路径存在:会把前缀误判成完整单词。
- startsWith 也检查 end:math 还没插入时 ma 会被误判为 false。
- 插入时忘记在终点打 end:之后 search 永远失败。
- 用整词哈希表代替 Trie:startsWith 需要扫描所有单词,退化为 O(N·L)。
代码与演示
演示输入
依次插入 "map"、"math"、"mate"、"max";查询 search("mat")、startsWith("ma")、search("math")、search("max")根节点不代表字符;插入单词就是沿字符路径建节点,并在最后一个节点打 end。
Trie · 每个节点代表一个字符前缀
查看 C++ 代码
延伸阅读
- Trie 的节点通常用 26 个指针的数组;字符集很大时可以改用哈希表,节省空间。
- 前缀树常用于自动补全、拼写检查、单词搜索 II(212)等需要“共享前缀”的场景。