题目大意
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
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
说明:
- 你可以假设所有的输入都是由小写字母 a-z 构成的。
- 保证所有输入均为非空字符串。
解题思路
我们首先创建一个前缀树节点TrieNode:
struct TrieNode
{
int path; // 通过当前节点的单词有多少个(包含以当前节点结尾)
int end; // 以当前节点结尾的单词有多少个
vector<TrieNode * >map; // 对应26个小写字母,初始化全部为空
TrieNode()
{
path = 0;
end = 0;
map = vector<TrieNode*>(26, nullptr);
}
};
初始化:
创建一个空节点,不对应任何字母,该节点的map才对应26个小写字母。
TrieNode * root = new TrieNode();
对于插入操作:
void insert(string word) {
// 插入单词为空
if (word.size() == 0)
return;
// 设置一个指针指向前缀树的根节点
TrieNode * node = root;
int index = 0;
// 遍历要插入单词的所有字母
for (int i = 0; i < word.size(); i++)
{
index = word[i] - 'a';
// 如果还没有通向该字母的路径,则新创建一个节点
if (node->map[index] == nullptr)
node->map[index] = new TrieNode();
// node指向该字母对应的节点
node = node->map[index];
// 经过该字母的路径数量+1
node->path++;
}
// 当插入单词遍历完成后,以该字母为最后一个单词的数量+1
node->end++;
}
对于查找:
bool search(string word) {
if (word.size() == 0)
return false;
TrieNode * node = root;
int index;
for (int i = 0; i < word.size(); i++)
{
index = word[i] - 'a';
// 如果当前字母的节点为空,表示并不存在一条路径可以走完当前单词
if (node->map[index] == nullptr)
return false;
// 如果当前字母对应节点存在,则指向当前字母所对应的节点
node = node->map[index];
}
// 由于我们是查找完整的单词,例如'abc',我们之前插入过'abcd'。
// 因此c节点的end并不是0(插入的单词不以c结尾),所以没有'abc'这个单词
return node->end != 0;
}
对于前缀开始查找:
bool startsWith(string prefix) {
if (prefix.size() == 0)
return false;
TrieNode * node = root;
int index = 0;
for (int i = 0; i < prefix.size(); i++)
{
index = prefix[i] - 'a';
if (node->map[index] == nullptr)
return false;
node = node->map[index];
}
// 不同于search,只要该前缀存在即可
// search需要完成的单词存在
return node->path;
}