字典树详解(Trie树,前缀树)

首先说明一点的是,文章里面用的是伪代码,好让各种语言的读者整明白了,我相信最后各位读者自己也可以写出来,最后保险起见。我会贴出个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树和散列表一样都是利用空间换取时间的一种策略
优点:

  1. 插入和查询(找一个单词是否存在)的效率很高,均是O(m),其中 m 是待插入/查询的 字符串长度 。

缺点:

  1. 如果字母的种数为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 例题

  1. leetcode题目《208. 实现 Trie (前缀树)》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/
  2. leetcode题目《820. 单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/
    在这里插入图片描述

7. 参考资料

  1. leetcode题目《 208. 实现 Trie (前缀树)》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/
  2. leetcode题目题解《 208. 实现 Trie (前缀树)的官方题解》,网址:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode/
  3. leetcode题目《820. 单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/
  4. 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/
  5. leetcode题目820. 单词的压缩编码官方题解《单词的压缩编码》,网址:https://leetcode-cn.com/problems/short-encoding-of-words/solution/dan-ci-de-ya-suo-bian-ma-by-leetcode-solution/
  6. 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
  7. 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
  8. CSDN博主hackbuteer1的《Trie树详解及其应用》,网址:https://blog.csdn.net/hackbuteer1/article/details/7964147
  9. CSDN博主皮小猪的时光的《字典树Trie》,网址:https://blog.csdn.net/hihozoo/article/details/51248823
  10. CSDN博主虎纠卫的《理解区块链》,网址:https://blog.csdn.net/csolo/article/details/52858236
  11. CSDN博主qq_33935254的《深入浅出以太坊MPT(Merkle Patricia Tree)》,网址:https://blog.csdn.net/qq_33935254/article/details/55505472
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章