shiqi.cui<cuberub@gmail.com>
May 24, 2009
1. Trie
Trie是一種搜索樹,因“Retrieval”而得名。在以Trie樹組織的詞典里,所有詞條的公共前綴是壓縮存儲的,即只會存儲一份,所以又稱前綴樹。如圖所示:

Trie可以理解為確定有限狀態自動機,即DFA。在Trie樹中,每個節點表示一個狀態,每條邊表示一個字符,從根節點到葉子節點經過的邊即表示一個詞條。查找一個詞條最多耗費的時間只受詞條長度影響,因此Trie的查找性能是很高的,跟哈希算法的性能相當。
2. Trie存儲方式
Trie可以按照樹的方式存儲。每個節點包含n個指針,分別指向n個后續節點,每條邊對應着一個輸入字符。這樣,每個節點的指針個數是跟字符表的大小相關的。如果按照鏈表的方式組織n個指針,查詢的效率會比較低;如果以定長數組表示n個指針,占用的空間會比較大,基本是不可接受的。
Trie也可以按照DFA的方式存儲,即表示為轉移矩陣。行表示狀態,列表示輸入字符,(行, 列)位置表示轉移狀態。這種方式的查詢效率很高,但由於稀疏的現象嚴重,空間利用效率很低。也可以采用壓縮的存儲方式即鏈表來表示狀態轉移,但查詢效率無法滿足要求。
為了解決上面的問題,有學者依次設計出了Four-Array Trie,Triple-Array Trie和Double-Array Trie結構,其得名源於內部采用的數組的個數。
3. Double-Array Trie
Double-Array Trie包含base和check兩個數組。base數組的每個元素表示一個Trie節點,即一個狀態;check數組表示某個狀態的前驅狀態。
base和check的關系滿足下述條件:
base[s] + c = t
check[t] = s
其中,s是當前狀態的下標,t是轉移狀態的下標,c是輸入字符的數值。如圖所示:

4. 查詢過程
根據上述公式,查找某個字符串就非常簡單。
假設初始狀態為t0,字符序列是(c1, c2, …, cn)。那么,輸入c1后的狀態為t1 = base[t0] + c1,以此類推。
如果到某個狀態是不合法的,那么查詢失敗;如果轉到狀態tn = base[tn-1] + c,並且tn是結束狀態,那么查詢成功;如果tn不是結束狀態,那么查詢失敗。
5. 構建過程
首先,初始化base和check數組,元素默認值是0。隨機確定初始狀態t0及其base值,如t0=0, base[t0]=1。
對於插入詞條,計算輸入每個字符后的base位置。
如果該位置為空,則表示該位置可以插入,然后轉到下一個字符;
如果該位置已有值,表示該位已經被其他的狀態占用,這樣需要調整其前驅狀態的base值,以保證狀態不會沖突,這個過程稱為relocate。
