首先說明一點的是,文章裏面用的是僞代碼,好讓各種語言的讀者整明白了,我相信最後各位讀者自己也可以寫出來,最後保險起見。我會貼出個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