文章目录
-
- 一、题目描述
- 二、问题分析
-
- [Trie 结构示意图](#Trie 结构示意图)
- 三、设计思路
-
- 节点结构(TrieNode)
- [Trie 的核心操作流程](#Trie 的核心操作流程)
-
- [1. 插入单词 insert(word)](#1. 插入单词 insert(word))
- [2. 查找单词 search(word)](#2. 查找单词 search(word))
- [3. 前缀匹配 startsWith(prefix)](#3. 前缀匹配 startsWith(prefix))
- 四、复杂度分析
- [五、Java 实现代码](#五、Java 实现代码)
- 六、运行示意图(时序图)
- 七、优化与扩展
✅ 题目链接:LeetCode - Implement Trie (Prefix Tree)
难度:中等
适合人群:掌握数据结构基础、希望理解字符串搜索机制的开发者
一、题目描述
实现一个 前缀树(Trie),支持以下三种操作:
insert(word):插入一个单词。search(word):判断单词是否存在。startsWith(prefix):判断是否存在某个单词以给定前缀开始。
示例输入与输出:
text
输入:
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
二、问题分析
Trie 是一种用于 高效存储和查找字符串集合 的数据结构。
它的核心思想是将字符串以「公共前缀」的方式组织在一棵树中,避免重复存储。
Trie 结构示意图
Root
a
p
l
e
b
t
在上图中,我们插入了 "apple"、"app"、"bat" 三个单词。
可见 "app" 是 "apple" 的前缀,两者共享节点,从而节省存储空间。
三、设计思路
节点结构(TrieNode)
- 每个节点包含:
children:保存连接到下一个字符的节点(通常用数组或哈希表)。isEnd:标记当前节点是否为某个单词的结尾。
Trie 的核心操作流程
1. 插入单词 insert(word)
否
是
否
是
开始
取根节点
当前字符是否存在?
创建新节点
进入下一个节点
是否到达末尾?
标记 isEnd=true
结束
2. 查找单词 search(word)
逐字符遍历节点:
- 若某个字符路径不存在,返回
false - 若遍历完成且最终节点
isEnd == true,返回true
3. 前缀匹配 startsWith(prefix)
与 search 类似,但不关心 isEnd,只需所有前缀节点存在即可返回 true
四、复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| insert | O(L) | O(L * α) | L为单词长度,α为字母表大小(如26) |
| search | O(L) | O(1) | 遍历字符路径 |
| startsWith | O(L) | O(1) | 仅检查前缀存在 |
由于 Trie 节点彼此共享公共前缀,它在存储大量相似单词时效率极高。
五、Java 实现代码
下面给出基于数组结构的 Java 实现,结构清晰,性能稳定:
java
class TrieNode {
TrieNode[] children;
boolean isEnd;
public TrieNode() {
children = new TrieNode[26];
isEnd = false;
}
}
public class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// 插入单词
public void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
int index = c - 'a';
if (node.children[index] == null) {
node.children[index] = new TrieNode();
}
node = node.children[index];
}
node.isEnd = true;
}
// 搜索完整单词
public boolean search(String word) {
TrieNode node = find(word);
return node != null && node.isEnd;
}
// 判断是否存在以指定前缀开始的单词
public boolean startsWith(String prefix) {
TrieNode node = find(prefix);
return node != null;
}
// 辅助函数:根据字符串查找节点
private TrieNode find(String prefix) {
TrieNode node = root;
for (char c : prefix.toCharArray()) {
int index = c - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
六、运行示意图(时序图)
TrieNode Trie User TrieNode Trie User insert("apple") 为每个字符创建节点 a ->> p ->> p ->> l ->> e 返回最后节点 插入完成 search("app") 查找 a ->> p ->> p 查找结束 返回 false(未标为结束) startsWith("app") 查找前缀路径 前缀存在 返回 true
七、优化与扩展
- 可扩展字符集: 如果需要支持 Unicode 或大小写混合,可以将
children改为Map<Character, TrieNode>。 - 删除操作: 可通过递归方式实现,当某节点无其他分支且非单词结尾时可清理。
- 前缀搜索应用: 如自动补全、词频统计等。
Trie 是一种非常有价值的数据结构,在 搜索引擎自动补全、拼写校验、字符串匹配 等场景中广泛应用。
其核心优势在于 以空间换时间,在字符层级组织信息,快速定位前缀路径。