首先说明一点的是,文章里面用的是伪代码,好让各种语言的读者整明白了,我相信最后各位读者自己也可以写出来,最后保险起见。我会贴出个JAVA,python,C++语言版本实现的链接,避免篇幅过长的问题。(博主第一次写数据结构的伪代码,受到C++风格影响比较深,望理解)。
文章目录
1. 字典树究竟是个什么鬼
字典树又名前缀树,Trie树,是一种存储大量字符串的多叉树形数据结构,一个节点存储一个英文字符,一个条路(从根节点到叶子节点的路径)存储了一个单词,整个树先序遍历过后就是一个短句。
下面演示了Happy Hour - incoming(酒桶)这句短句在字典树上的存储方式。
很明显,每条从根节点到叶子节点的路径都构成了单词(不同颜色的圈应该很清晰)。每一个节点下面应该连接子26个节点(26个英文字母),没有节点的数据将属性设置为空罢了。但是这里我没有理由将26个字母全部都标记出来,那不是浪费位置,在搞笑嘛。
如果用对比理解的思想的话,其实这个鬼东西和散列表差不多,只不过散列表是key-value的映射方式,而字典树(Trie树)的key以字符串的形式呈现。散列表的关键字冲突,放在Trie树上就是,一个关键词下面挂着许多小的节点。
1.1 Trie树的特性
Trie树的基本性质可以归纳为:
(1)根节点不包含字符,除根节点以外,每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该所节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。
(4)空间换取时间策略
(5)速度非常快,插入和查询(找一个单词是否存在)的效率很高,均是O(m)
(6)内存空间消耗比较大
2. Trie树的优缺点
Trie树和散列表一样都是利用空间换取时间的一种策略。
优点:
- 插入和查询(找一个单词是否存在)的效率很高,均是O(m),其中 m 是待插入/查询的 字符串长度 。
缺点:
- 如果字母的种数为m,那么树中的每个子节点的出度为m,浪费了许多内存空间,空间换时间可以理解。
3. 字典树的应用(其实都是在存储字符串方面)
1.最典型的例子是搜索引擎:
2.Trie树的进化版本:Merkle Patricia Tree,在区域链里面有许多应用。不过这两个东西博主属实不懂,我不敢乱说贴出两个链接,供了解一下。
CSDN博主虎纠卫的《理解区块链》,
网址:https://blog.csdn.net/csolo/article/details/52858236
CSDN博主qq_33935254的《深入浅出以太坊MPT(Merkle Patricia Tree)》,
网址:https://blog.csdn.net/qq_33935254/article/details/55505472
4. 构建字典树(伪代码)
字典树的常见的操作有插入、查询(删除操作,当然也是比较常见的,但是字典树最主要的功能还是存储字符串和查找单词这一方面),查找前缀。
4.1 初始化字典树,定义字典树的属性
伪代码中的注释可能不是特别清楚,没什么关系我用图。
1. define Trie tree: //定义字典树
2. Property (property,属性的意思): //定义字典树的属性,类似于C++的private,以及C语言的结构
3. Tag isEnd //设置isEnd作为完成一个完整单词的标志,一段路径一个单词
4. Trie* next[26] //子节点A B C ... Z,英文字母不就26个嘛,按26个字母来存储单词
5.
6.
7.
8. Operation: //定义字典树的操作
9. Trie-Initialization ( ) //初始化功能,The Initialize function
10. isEnd = false //一个单词处理完了才把isEnd设置为True,未弄好前isEnd设置为false
11. every-elements-in-the-next = NULL //把next(存储字母的东西)里面每一个元素(26个字母)设置为 NULL(空)
4.2 字典树的插入操作
插入操作的核心思想从根开始,沿着一个单词的各个字母往下走,单词中的一个字母对应的就是树的一个节点,最后单词走完了设置isEnd,表示单词完成。
12.
13.
14. Trie-Insert-Operation ( string word ) //将一个单词(word)插入到字典树里面去
15. node = Trie-root //设置node作为移动节点,首先从根节点往下走
16. for every letters of word //处理一个单词上面的每一个字母
17. if the letter in the next node is NULL //如果该字母在子节点上面为空,也就是与之前插入的单词没有重复
18. the-next-node-of-letter = Trie-Initialization //对letter所对应子节点next-node进行延伸,往下走
19. end if
20. node = the-next-node-of-letter //移动节点,往下走,继续存储下个字母
21. end for
22. isEnd = true //最后顺手设置isEnd表示一个完整的路径(完整的单词)
4.3 查找单词操作
查找操作的核心思想,与插入操作一样,都是从根往下走,遍历一个单词,如果遍历的时候,树中所对应单词(word)的节点为空,那么说明这个单词不存在,返回false,遍历完了返回isEnd标志。
23.
24.
25. Trie-Search-Word-Operation ( string word ) //查找该字典树是否有这个完整的单词
26. node = Trie-root //设置node作为移动节点,首先从根节点往下走
27. for every letters of word //处理一个单词上面的每一个字母
28. node = the-next-node-of-letter //移动节点,因为根一开始是木有的单词的
29. if the node is NULL //一个字母错就全错,也就是该单词不存在(所以说别打错字)
30. return false //告诉程序员:404,这个单词是不存在的,没有这个单词
31. end if
32. end for
33. return isEnd //告诉程序员:我已经找完一个单词啦
4.4 查找前缀操作
由于和查找单词逻辑上差不多,所以不打算详细讲了
34.
35.
36. Trie-Search-Prefix-Operation (string prefix (prefix,前缀的意思)) //逻辑上与找单词一样,只是后面一点不同
37. node = Trie-root
38. for every letters of word
39. node = the-next-node-of-letter
40. if the node is NULL
41. return false
42. end if
43. end for
44. return true //告诉程序员:我找到了,哈哈哈哈哈
5. 完整的伪代码
1. define Trie tree: //定义字典树
2. Property (property,属性的意思): //定义字典树的属性,类似于C++的private,以及C语言的结构
3. Tag isEnd //设置isEnd作为完成一个完整单词的标志,一段路径一个单词
4. Trie* next[26] //子节点A B C ... Z,英文字母不就26个嘛,按26个字母来存储单词
5.
6.
7.
8. Operation: //定义字典树的操作
9. Trie-Initialization ( ) //初始化功能,The Initialize function
10. isEnd = false //一个单词处理完了才把isEnd设置为True,未弄好前isEnd设置为false
11. every-elements-in-the-next = NULL //把next(存储字母的东西)里面每一个元素(26个字母)设置为 NULL(空)
12.
13.
14. Trie-Insert-Operation ( string word ) //将一个单词(word)插入到字典树里面去
15. node = Trie-root //设置node作为移动节点,首先从根节点往下走
16. for every letters of word //处理一个单词上面的每一个字母
17. if the letter in the next node is NULL //如果该字母在子节点上面为空,也就是与之前插入的单词没有重复
18. the-next-node-of-letter = Trie-Initialization //对letter所对应子节点next-node进行延伸,往下走
19. end if
20. node = the-next-node-of-letter //移动节点,往下走,继续存储下个字母
21. end for
22. isEnd = true //最后顺手设置isEnd表示一个完整的路径(完整的单词)
23.
24.
25. Trie-Search-Word-Operation ( string word ) //查找该字典树是否有这个完整的单词
26. node = Trie-root //设置node作为移动节点,首先从根节点往下走
27. for every letters of word //处理一个单词上面的每一个字母
28. node = the-next-node-of-letter //移动节点,因为根一开始是木有的单词的
29. if the node is NULL //一个字母错就全错,也就是该单词不存在(所以说别打错字)
30. return false //告诉程序员:404,这个单词是不存在的,没有这个单词
31. end if
32. end for
33. return isEnd //告诉程序员:我已经找完一个单词啦
34.
35.
36. Trie-Search-Prefix-Operation (string prefix (prefix,前缀的意思)) //逻辑上与找单词一样,只是后面一点不同
37. node = Trie-root
38. for every letters of word
39. node = the-next-node-of-letter
40. if the node is NULL
41. return false
42. end if
43. end for
44. return true //告诉程序员:我找到了,哈哈哈哈哈
45.
46. Finish
47. //原谅我可能位置上面有点偏移
6. 具体实现以及对应例题
6.1 C++实现
//上面伪代码的注释很清楚了,这里我就偷一波懒
class Trie {
private:
bool isEnd;
Trie* children[26];
public:
Trie() {
isEnd = false;
memset(children,0,sizeof(children));
}
void insert(string word) {
Trie* node = this;
for (auto letter : word) {
if (node->children[letter - 'a'] == NULL) {
node->children[letter - 'a'] = new Trie();
}
node = node->children[letter -'a'];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this;
for (auto letter : word) {
node = node->children[letter - 'a'];
if (node == NULL) {
return false;
}
}
return node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = this;
for (auto letter : prefix) {
node = node->children[letter - 'a'];
if (node == NULL) {
return false;
}
}
return true;
}
};
6.2 例题
- leetcode题目《208. 实现 Trie (前缀树)》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/
- leetcode题目《820. 单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/
7. 参考资料
- leetcode题目《 208. 实现 Trie (前缀树)》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/
- leetcode题目题解《 208. 实现 Trie (前缀树)的官方题解》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode/
- leetcode题目《820. 单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/
- leetcode题目820. 单词的压缩编码,用户题解Sweetiee 🍬的《99% Trie 吐血攻略,包教包会》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/solution/99-java-trie-tu-xie-gong-lue-bao-jiao-bao-hui-by-s/
- leetcode题目820. 单词的压缩编码官方题解《单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/solution/dan-ci-de-ya-suo-bian-ma-by-leetcode-solution/
- CSDN博主Chiclaim的《数据结构与算法(十一)Trie字典树》,网址:https://blog.csdn.net/johnny901114/article/details/80711441?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
- CSDN博主向前走别回头的《字典树(前缀树)》,网址:https://blog.csdn.net/weixin_39778570/article/details/81990417?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
- CSDN博主hackbuteer1的《Trie树详解及其应用》,网址:https://blog.csdn.net/hackbuteer1/article/details/7964147
- CSDN博主皮小猪的时光的《字典树Trie》,网址:https://blog.csdn.net/hihozoo/article/details/51248823
- CSDN博主虎纠卫的《理解区块链》,网址:https://blog.csdn.net/csolo/article/details/52858236
- CSDN博主qq_33935254的《深入浅出以太坊MPT(Merkle Patricia Tree)》,网址:https://blog.csdn.net/qq_33935254/article/details/55505472