添加与搜索单词 - 数据结构设计
难度:
标签:
题目描述
请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
实现词典类 WordDictionary
:
WordDictionary()
初始化词典对象void addWord(word)
将word
添加到数据结构中,之后可以对它进行匹配bool search(word)
如果数据结构中存在字符串与word
匹配,则返回true
;否则,返回false
。word
中可能包含一些'.'
,每个.
都可以表示任何一个字母。
示例:
输入: ["WordDictionary","addWord","addWord","addWord","search","search","search","search"] [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]] 输出: [null,null,null,null,false,true,true,true] 解释: WordDictionary wordDictionary = new WordDictionary(); wordDictionary.addWord("bad"); wordDictionary.addWord("dad"); wordDictionary.addWord("mad"); wordDictionary.search("pad"); // 返回 False wordDictionary.search("bad"); // 返回 True wordDictionary.search(".ad"); // 返回 True wordDictionary.search("b.."); // 返回 True
提示:
1 <= word.length <= 25
addWord
中的word
由小写英文字母组成search
中的word
由 '.' 或小写英文字母组成- 最多调用
104
次addWord
和search
代码结果
运行时间: 660 ms, 内存: 0.0 MB
/*
* 题目思路:
* 1. 使用 Trie 树存储添加的单词,每个节点代表一个字母。
* 2. 利用 Java Stream 流式操作优化搜索通配符 '.' 的逻辑。
*/
import java.util.*;
import java.util.stream.*;
public class WordDictionary {
private TrieNode root;
public WordDictionary() {
root = new TrieNode();
}
// Trie 树节点类
private class TrieNode {
private TrieNode[] children;
private boolean isEnd;
public TrieNode() {
children = new TrieNode[26];
isEnd = false;
}
}
// 添加单词到 Trie 树
public void addWord(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
if (node.children[c - 'a'] == null) {
node.children[c - 'a'] = new TrieNode();
}
node = node.children[c - 'a'];
}
node.isEnd = true;
}
// 查找单词或通配符组合
public boolean search(String word) {
return search(word, root);
}
private boolean search(String word, TrieNode node) {
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (c == '.') {
return Arrays.stream(node.children)
.filter(Objects::nonNull)
.anyMatch(child -> search(word.substring(i + 1), child));
} else {
if (node.children[c - 'a'] == null) {
return false;
}
node = node.children[c - 'a'];
}
}
return node.isEnd;
}
}
解释
方法:
该题解使用字典树(Trie)的数据结构来解决问题。在添加单词时,将单词的每个字符作为字典树的节点插入。在搜索单词时,从字典树的根节点开始,逐个字符进行匹配。如果遇到 '.',则对当前节点的所有子节点进行递归搜索。如果搜索到字典树的叶子节点,且该节点标记为单词结尾,则说明找到了匹配的单词。
时间复杂度:
添加单词:O(L),搜索单词:O(M)
空间复杂度:
O(N)
代码细节讲解
🦆
在`search`函数中使用递归搜索时,如果遇到'.'字符,为什么选择遍历所有子节点而不是选择特定的一些子节点进行搜索?
▷🦆
字典树中的`is_word`属性具体是如何表示一个单词结束的?如果在遍历到最后一个字符时`is_word`不为真,是如何处理的?
▷🦆
在添加单词到字典树时,如果单词中包含重复字符怎样处理?例如添加单词'good'和'goody',字典树的结构会如何变化?
▷🦆
对于`search_word`函数中的递归调用,如果输入单词非常长或字典树结构很深,有没有可能导致栈溢出?如何优化以防止这种情况?
▷相关问题
实现 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 * 104
次