实现 Trie (前缀树) II
难度:
标签:
题目描述
代码结果
运行时间: 177 ms, 内存: 28.9 MB
/*
题目思路:
Trie树是一种树形结构,用于高效地存储和检索字符串集合中的键。
主要操作包括插入一个字符串、搜索一个字符串和搜索具有某前缀的字符串。
在Trie树中,每个节点表示字符串中的一个字符,根节点表示空字符。
使用Java Stream的方式来实现Trie树的功能。
*/
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
class TrieNode {
public Map<Character, TrieNode> children;
public boolean isEndOfWord;
public TrieNode() {
children = new HashMap<>();
isEndOfWord = false;
}
}
class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// 插入一个单词到Trie树
public void insert(String word) {
TrieNode node = root;
word.chars().mapToObj(c -> (char) c).forEach(c -> {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
});
node.isEndOfWord = true;
}
// 搜索Trie树中是否存在某个单词
public boolean search(String word) {
TrieNode node = root;
return word.chars().mapToObj(c -> (char) c).allMatch(c -> {
if (node.children.containsKey(c)) {
node = node.children.get(c);
return true;
} else {
return false;
}
}) && node.isEndOfWord;
}
// 搜索Trie树中是否存在以某个前缀开头的单词
public boolean startsWith(String prefix) {
TrieNode node = root;
return prefix.chars().mapToObj(c -> (char) c).allMatch(c -> {
if (node.children.containsKey(c)) {
node = node.children.get(c);
return true;
} else {
return false;
}
});
}
}
解释
方法:
这个题解实现了一个 Trie (前缀树) 数据结构,并支持插入、查询和删除字符串的操作。每个节点包含一个映射表 children 来保存子节点,一个 count 变量记录经过此节点的单词数量,和一个 endCount 变量记录以此节点结尾的单词数量。在 insert 方法中,遍历插入的单词的每个字符,如果字符不存在于当前节点的 children 中,则创建一个新的 Trie 节点。同时更新经过的每个节点的 count 值。countWordsEqualTo 方法通过遍历单词找到对应的节点并返回其 endCount 值,表示完全匹配该单词的数量。countWordsStartingWith 方法遍历前缀找到对应节点并返回其 count 值,表示以该前缀开始的单词数量。erase 方法用于删除一个单词,它减少经过每个节点的 count 值和最终节点的 endCount 值。
时间复杂度:
O(m)
空间复杂度:
O(∑w)
代码细节讲解
🦆
在 `countWordsEqualTo` 方法中,如果某字符不存在于 Trie 中,直接返回 0。这种设计是否意味着所有 Trie 操作都假设输入字符串完全由已知字符集构成?
▷🦆
Trie 的 `count` 和 `endCount` 变量如何准确反映删除操作后的单词频率?即在某些节点的 `count` 可能减少到零的情况下,这些节点是否还保留在 Trie 中?
▷