参考资料
- 甜姨的力扣题解:https://zhuanlan.zhihu.com/p/120150816
- 力扣官方题解:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode/
- https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/trie-tree-de-shi-xian-gua-he-chu-xue-zhe-by-huwt/
1.2.资料都是基于Java来对Trie进行实现,3.是c++的实现,原理都介绍的非常棒!
Trie Introduction(介绍字典树)
Trie也叫字典树、前缀树、单词查找树等等,它常用来存储单词(和语种无关),相比于HashMap等操作,Trie能在存储多个具有相同前缀的键时,使用较少的空间。并且Tire树只需要O(M)的时间复杂度,其中M为键长。
下图演示了一个保存8个单词的字典树结构,8个单词分别是:“A”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”。(蓝色节点表示单词的结尾字符)
【示例来自参考资料1】
说明
root节点为Null;
把从root节点出发到蓝色节点的路径上所表示的字符连接起来,就构成一个单词;
从root节点出发,都是单词列表里某个单词/某些单词的前缀;
如果某个字符串没有出现在这棵树的路径上,那说明其不是所给出单词列表的前缀。
然后相比一般的多叉树,Trie在节点的数据结构设计上也有很多不同。
class Trie{
private:
bool flag;
Trie* next[R];
};
其中val表示的是该节点是否为尾节点(是不是对应键的结尾),next[R]
表示的是指向R个子节点的链接。比如我们现在要表示英语单词的话,R就为26。
说明
因为flag
只用来表示该节点是否为一个单词的结尾,然后next[R]
可以看做是指向26个字母的映射表,保存了对于当前节点而言,下一个可能出现的字符链接。所以,上面的图并不是字典树真正的存储形式,我们看下图:
这是一个包含三个单词‘sea’,‘she’,‘sells’的字典树完整形式。
【示例来自参考资料3】
可以看到,Trie中一般含有大量的空链接。
C++实现
class Trie{
private:
// 定义节点
bool flag = false;
Trie* next[26] = {nullptr};
public:
Trie(){} // 构造函数
void insert(string word){ // 插入
Trie* node = this;
for(int i=0; i<word.length(); i++){
char c = word[i];
if(node->next[c-'a']==nullptr){
node->next[c-'a'] = new Trie();
}
node = node->next[c-'a'];
}
node->flag = true; // 单词串的尾节点为true
}
bool search(string word){ // 查找单词是否存在Trie中
Trie* node = this;
for(int i=0; i<word.length(); i++){
char c = word[i];
if(node->next[c-'a']==nullptr) return false;
node = node->next[c-'a'];
}
return node->flag; // 如果该节点是串尾节点,则为true
}
bool starsWith(string prefix){ //查找前缀
Trie * node = this;
for(int i=0; i<prefix.length(); i++){
char c = prefix[i];
if(node->next[c-'a']==nullptr) return false;
node = node->next[c-'a'];
}
return true;
}
};
说明:
最常用的有三个操作,插入单词、搜索单词是否在字典树中、搜索前缀是否在字典树中。
插入单词:node指向根节点,从单词的第一个字符开始判断,该字符是否在节点指向的字母映射表中出现(不为nullptr
空指针),如果没有出现过就创建对应的内存空间,然后node指向对应字母的链接。重复操作,直到该单词全部插入完,再另最后的尾结点node->flag=true()
(表示一个单词的结尾)。有点类似链表的插入。
查找单词是否在字典树中:同样,node先指向根节点,然后从单词的第一个字符开始判断,该字符是否在node指向的字母映射表中出现,如果没有出现直接return false
,否则node指向对应字母的链接,重复操作,直至单词全部查询完。最后再判断node->flag == true?
,如果不为true,则表示并不是单词的结尾(该单词没有出现在字典树中,只是前缀包含了该单词)。
查找前缀是否出现在字典树中 :跟上面查找单词是一样的,只是最后不用再判断flag。
对应的LeetCode练习:LeetCode208. 实现Trie(前缀树)
Trie 应用
-
自动补全
-
拼写检查
-
IP路由