思路:
- 前缀树又称字典树,用来快速查询新的字符串是否在当前的字典中,如果没有,就将其插入到字典中。用途就是模糊搜索(比如代码补全),本质上是一棵26叉树。
- 对于树中的每一个节点,其数据结构都是一个
bool
数据 + 26个分叉指针。如果父亲节点的子节点不为空,说明该子节点索引对应的字符存在字典中。
bool is_end;
Trie* next[26];
为了便于逻辑上的理解,主流的画法是不画出空节点。
- 分成两个操作:插入、查询
- 插入操作,一直往下搜索,如果遇到
nullptr
,就逐个把剩下的字符插入到字典树当中
void insert(string word) {
Trie* node = this;
for (char ch: word) {
if (node->next[ch - 'a'] == nullptr) {
// 父亲节点有子节点了,就说明当前位置存储了子节点索引所对应的字符
node->next[ch - 'a'] = new Trie();
}
node = node->next[ch - 'a'];
}
node->is_end = true;
}
- 查询操作更加直观,顺着数组中的每一个字母不断往下搜索,如果遇到了
nullptr
,那么就说明数组中的所有字符并没有完全存在字典树当中,返回false
,如果读完了整个数组,那么还要判断当前位置的is_end
,说不定现在的数组,只是之前的字典树中存储的记录的一部分呢。
bool search(string word) {
Trie* node = this;
for (char ch: word) {
node = node->next[ch - 'a'];
if (node == nullptr) {
return false;
}
}
return node->is_end;
}
- 搜索前缀更加简单,和普通搜索差不多,最后改成返回
true
即可。
bool startsWith(string prefix) {
Trie* node = this;
for (char ch: prefix) {
node = node->next[ch - 'a'];
if (node == nullptr) {
return false;
}
}
return true;
}
代码:
class Trie {
private:
bool is_end;
Trie* next[26];
public:
Trie() {
is_end = false;
memset(next, 0, sizeof(next));
}
void insert(string word) {
Trie* node = this;
for (char ch: word) {
if (node->next[ch - 'a'] == nullptr) {
// 父亲节点有子节点了,就说明当前位置存储了子节点索引所对应的字符
node->next[ch - 'a'] = new Trie();
}
node = node->next[ch - 'a'];
}
node->is_end = true;
}
bool search(string word) {
Trie* node = this;
for (char ch: word) {
node = node->next[ch - 'a'];
if (node == nullptr) {
return false;
}
}
return node->is_end;
}
bool startsWith(string prefix) {
Trie* node = this;
for (char ch: prefix) {
node = node->next[ch - 'a'];
if (node == nullptr) {
return false;
}
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/