字典樹詳解(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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章