一、字典樹的概念
Trie樹,又稱字典樹,單詞查找樹或者前綴樹,是一種用於快速檢索的多叉樹結構,如英文字母的字典樹是一個26叉樹,數字的字典樹是一個10叉樹。
與二叉查找樹不同,Trie樹的鍵不是直接保存在節點中,而是由節點在樹中的位置決定。一個節點的所有子孫都有相同的前綴,也就是這個節點對應的字符串,而根節點對應空字符串。一般情況下,不是所有的節點都有對應的值,只有葉子節點和部分內部節點所對應的鍵才有相關的值。
Trie樹優點是最大限度地減少無謂的字符串比較,查詢效率比較高。核心思想是空間換時間,利用字符串的公共前綴來降低查詢時間的開銷以達到提高效率的目的。
(1) 插入、查找的時間復雜度均為O(N),其中N為字符串長度。
(2) 空間復雜度是26^n級別的,非常龐大(可采用雙數組實現改善)。
它有3個基本性質:
根節點不包含字符,除根節點外每一個節點都只包含一個字符。
從根節點到某一節點,路徑上經過的字符連接起來,為該節點對應的字符串。
每個節點的所有子節點包含的字符都不相同。
二、Trie樹和DFA,確定有限狀態自動機
trie樹實際上是一個DFA,通常用轉移矩陣表示。行表示狀態,列表示輸入字符,(行,列)位置表示轉移狀態。這種方式的查詢效率很高,但由於稀疏的現象嚴重,空間利用效率很低。也可以采用壓縮的存儲方式即鏈表來表示狀態轉移,但由於要線性查詢,會造成效率低下。
三、Trie樹的實現
Trie樹的創建要考慮的是父節點如何保存孩子節點,主要有鏈表和數組兩種方式:
(1)使用節點數組,因為是英文字符,可以用Node[26]來保存孩子節點(如果是數字我們可以用Node[10]),這種方式最快,但是並不是所有節點都會有很多孩子,所以這種方式浪費的空間太多
(2)用一個鏈表根據需要動態添加節點。這樣我們就可以省下不小的空間,但是缺點是搜索的時候需要遍歷這個鏈表,增加了時間復雜度。
給出一組單詞,inn, int, at, age, adv, ant, 我們可以得到下面的Trie:
可以看出:
每條邊對應一個字母。
每個節點對應一項前綴。葉節點對應最長前綴,即單詞本身。
單詞inn與單詞int有共同的前綴“in”, 因此他們共享左邊的一條分支,root->i->in。同理,ate, age, adv, 和ant共享前綴"a",所以他們共享從根節點到節點"a"的邊。
(1)查詢操作
查詢操作非常簡單。比如要查找int,順着路徑i -> in -> int就找到了。
(2)如何構建
Trie樹的構建也很簡單,無非是逐一把每則單詞的每個字母插入Trie。
插入前先看前綴是否存在。如果存在,就共享,否則創建對應的節點和邊。
比如要插入單詞add,就有下面幾步:
考察前綴"a",發現邊a已經存在。於是順着邊a走到節點a。
考察剩下的字符串"dd"的前綴"d",發現從節點a出發,已經有邊d存在。於是順着邊d走到節點ad
考察最后一個字符"d",這下從節點ad出發沒有邊d了,於是創建節點ad的子節點add,並把邊ad->add標記為d。
四、字典樹應用場景
(1) 字符串檢索
事先將已知的一些字符串(字典)的有關信息保存到trie樹里,查找另外一些未知字符串是否出現過或者出現頻率。
舉例:
給出N 個單詞組成的熟詞表,以及一篇全用小寫英文書寫的文章,請你按最早出現的順序寫出所有不在熟詞表中的生詞。
給出一個詞典,其中的單詞為不良單詞。單詞均為小寫字母。再給出一段文本,文本的每一行也由小寫字母構成。判斷文本中是否含有任何不良單詞。例如,若rob是不良單詞,那么文本problem含有不良單詞。
(2)字符串最長公共前綴
Trie樹利用多個字符串的公共前綴來節省存儲空間,反之,當我們把大量字符串存儲到一棵trie樹上時,我們可以快速得到某些字符串的公共前綴。
舉例:
給出N 個小寫英文字母串,以及Q 個詢問,即詢問某兩個串的最長公共前綴的長度是多少?
解決方案:首先對所有的串建立其對應的字母樹。此時發現,對於兩個串的最長公共前綴的長度即它們所在結點的公共祖先個數,於是,問題就轉化為了離線(Offline)的最近公共祖先(Least Common Ancestor,簡稱LCA)問題。
而最近公共祖先問題同樣是一個經典問題,可以用下面幾種方法:
1. 利用並查集(Disjoint Set),可以采用采用經典的Tarjan 算法;
2. 求出字母樹的歐拉序列(Euler Sequence )后,就可以轉為經典的最小值查詢(Range Minimum Query,簡稱RMQ)問題了;
(3)排序
Trie樹是一棵多叉樹,只要先序遍歷整棵樹,輸出相應的字符串便是按字典序排序的結果。
比如給你N 個互不相同的僅由一個單詞構成的英文名,讓你將它們按字典序從小到大排序輸出。
(4) 作為其他數據結構和算法的輔助結構
如后綴樹,AC自動機等
(5)詞頻統計
trie樹在這里的應用類似哈夫曼樹,
比如詞頻統計使用哈希表或者堆都可以,但是如果內存有限,就可以用trie樹來壓縮空間,因為trie樹的公共前綴都是用一個節點保存的。
(6)字符串搜索的前綴匹配
trie樹常用於搜索提示。如當輸入一個網址,可以自動搜索出可能的選擇。當沒有完全匹配的搜索結果,可以返回前綴最相似的可能。
Trie樹檢索的時間復雜度可以做到n,n是要檢索單詞的長度,
如果使用暴力檢索,需要指數級O(N2)的時間復雜度。
參考: