前缀树的实现
何谓前缀树?
前缀树(字典树),使用树状的数据结构存储一个字典的所有单词。前缀树是一个多叉树,一个节点可能有多个子节点,除根结点外,每个节点代表字符串中的一个字符。字符串在前缀树中不一定终止于叶子节点,比如 'app'。如果前缀树路径到达某个节点时表示了一个完整的字符串,那么这个节点应有特殊的标识。一个存储字符串 "apple" "apply" "app" "huw" "oppo" "xiao" "mi" 的前缀树如图:
实现前缀树
实现一个前缀树 Trie ,它有下列操作:
- 函数 insert ,在前缀树中添加一个字符串
- 函数 search,查找字符串,如果前缀树中包含该字符串则返回 true,否则返回 false。
- 函数 startWith,查找字符串前缀。
节点的选择
子节点容器可以选择使用哈希表或数组。
- 在字符集大小固定且较小的情况下,数组可以提供紧凑的存储,没有额外的哈希表开销。数组可以保持元素有序性以及空间效率比较高。
- 哈希表比较灵活。
真实的结构:
cpp
class TrieNode {
public:
std::unordered_map<char, TrieNode*> children;
bool id_end_of_word;
TrieNode() : is_end_of_word(false) {}
};
插入
cpp
void insert(const std::string& word) {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
node->children[ch] = new TrieNode();
}
node = node->children[ch];
}
node->isEndOfWord = true;
}
查找
cpp
bool search(const std::string& word) {
TrieNode* node = root;
for (const auto& ch : word) {
if (node->children.find(ch) == node->children.end()) return false;
node = node->children[ch];
}
return node->is_end_of_word;
}
查找前缀
查找前缀与查找差别在于:可以直接返回 true。
cpp
bool startWith(const std::string& word) {
TrieNode* node = root;
for (const auto& ch : word) {
if (node->children.find(ch) == node->children.end()) return false;
node = node->children[ch];
}
return true;
}
完整代码
cpp
class Trie {
private:
TrieNode* root;
public:
Trie() {
root = new TrieNode();
}
void insert(const std::string& word) {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
node->children[ch] = new TrieNode();
}
node = node->children[ch];
}
node->isEndOfWord = true;
}
bool search(const std::string& word) const {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
return false;
}
node = node->children[ch];
}
return node->isEndOfWord;
}
};