acm社群(http://www.608088.com/)上的一篇文章,不錯。
一、 基本構造
trie樹是搜尋樹的一種,來自英文單詞"retrieval"的簡寫,可以建立有效的資料檢索組織結構,是中文匹配分詞演算法中詞典的一種常見實 現。它本質上是乙個確定的有限狀態自動機(dfa),每個節點代表自動機的乙個狀態。在詞典中這此狀態包括"詞字首","已成詞"等。
雙陣列trie(double-array trie)是trie樹的乙個簡單而有效的實現,由兩個整數陣列構成,乙個是base,另乙個是check。設陣列下標為i ,如果base,check均為0,表示該位置為空。如果base為負值,表示該狀態為詞語。check表示該狀態的前一狀 態,t=base+a, check[t]=i 。
下面舉例(源自《雙陣列trie(double-array trie)的資料結構與具體實現》)來說明用雙陣列trie(double-array trie)構造分詞演算法詞典的過程。假定詞表中只有「啊,阿根廷,阿膠,阿拉伯,阿拉伯人,埃及」這幾個詞,用trie樹可以表示為:
我們首先對詞表中所有出現的10個漢字進行編碼:啊-1,阿-2,唉-3,根-4,膠-5,拉-6,及-7,廷-8,伯-9,人-10。。對於每一 個漢字,需要確定乙個base值,使得對於所有以該漢字開頭的詞,在雙陣列中都能放下。例如,現在要確定「阿」字的base值,假設以「阿」開頭的詞的第 二個字序列碼依次為a1,a2,a3……an,我們必須找到乙個值i,使得 base[i+a1],check[i+a1],base[i+a2],check[i+a2]……base[i+an],check[i+an]均為 0。一旦找到了這個i,「阿」的base值就確定為i。用這種方法構建雙陣列trie(double-array trie),經過四次遍歷,將所有的詞語放入雙陣列中,然後還要遍歷一遍詞表,修改base值。因為我們用負的base值表示該位置為詞語。如果狀態i對 應某乙個詞,而且base=0,那麼令base=(-1)*i,如果base的值不是0,那麼令base= (-1)*base。得到雙陣列如下:下標
1 2
3 4
5 6
7 8
9 10
11 12
13 14
base -1
4 40 0
0 04 -9
4-11
-12 -4
-14check 0
0 00 0
0 02 2
2 38 10
13 詞綴
啊 阿埃 阿根
阿膠 阿拉
埃及阿根廷
阿拉伯阿拉伯人
用上述方法生成的雙陣列,將「啊」,「阿」,「埃」,「阿根」,「阿拉」,「阿膠」,「埃及」,「阿拉伯」,「阿拉伯人」,「阿根廷」均視為狀態。 每個狀態均對應於陣列的乙個下標。例如設「阿根」的下標為i=8,那麼check的內容是「阿」的下標,而base是「阿根廷」的下標的基 值。「廷」的序列碼為x=8,那麼「阿根廷」的下標為base+x=base[8]+8=12。
二、 基本操作與存在問題
1, 查詢
trie樹的查詢過程其實就是乙個dfa的狀態轉移過程,在雙陣列中實現起來比較簡單:只需按照狀態標誌進行狀態轉移即可.例如查詢「阿根廷」,先根據「 阿」的序列碼b=2,找到狀態「阿」的下標2,再根據「根」的序列碼d=4找到「阿根」的下標base+d=8,同時根據 check[base+d]=b,表明「阿根」是某個詞的一部分,可以繼續查詢。然後再找到狀態「阿根廷」。它的下標為y=12,此時 base[y]<0,check[y]=base+d=8,表明「阿根廷」在詞表中,查詢完畢。
查詢過程中我們可以看到,對於乙個詞語的查詢時間是只與它的長度相關的,也就是說它的時間複雜度為o(1).在漢語中,詞語以單字詞,雙字詞居多,超過三字的詞語少之又少.因此,用雙陣列構建的trie樹詞典查詢是理論上中文機械分詞中的最快實現。
2, 插入與刪除
雙陣列的缺點在於:構造調整過程中,每個狀態都依賴於其他狀態,所以當在詞典中插入或刪除詞語的時候,往往需要對雙陣列結構進行全域性調整,靈活性能較差。
將乙個詞語插入原有的雙陣列trie樹中,相當於對dfa增加乙個狀態。首先我們應根據查詢方法找出該狀態本應所處的位置,如果該位置為空,那好 辦,直接插入即可。如果該位置不為空。那麼我們只好按照構造時一樣的方法重新掃瞄得出該狀態已存在的最大字首狀態的base值,並由此依次得出該狀態後繼 結點的base值。在這其中還要注意check值的相應變化。
例如說,如果"阿拉根"某一天也成為了乙個詞,我們要在trie樹中插入這一狀態。按計算它的位置應在8,但8是乙個已成狀態.所以我們得重新確 定"阿拉"這一最大已成字首狀態的base值.重新掃瞄得出base[10]=11。這樣狀態15為"阿拉根",且base[15]為負(成 詞),check[15]=10;狀態20為"阿拉佰",且base[20]=-4,check=10。
這樣的處理其實是非常耗時間的,因為得依次對每乙個可能base值進行掃瞄來進行確定最大已成字首狀態的base值。這個確定過程在構造時還是基本 可以忍受的,畢竟你就算用上一,兩天來構造也沒有問題(只要你構造完後可以在效執行即可)。但在插入比較頻繁時,如果每次都需要那麼長的執行時間,那確實 是無法忍受的。
雙陣列刪除實現比較簡單,只需要將刪除詞語的對應狀態設為空即可――即base值,check均為設0。但它存在存在乙個空間效率的問題.例如,當 我們在上面刪除"埃及"這一詞語時,狀態11被設為空。而狀態10則成了乙個無用結點――它不成詞,而且在插入新詞時也不可重用。所以,隨著刪除的進行, 空狀態點和無用狀態點不斷增多,空間的利用率會不斷的降低。
三、 簡單優化
優化的基本思路是將雙陣列trie樹構建為一種動態檢索方法,從而解決插入和刪除所存在的問題。
1, 插入優化
在插入需要確定新的base值時,我們是只需要遍歷空狀態的。非空狀態的出現意味著某個base值嘗試的打敗,我們可以完全不必理會。所以,我們可以對所有的空狀態構建乙個序列,在確定base值時只需要掃瞄該序列即可。
對雙陣列中的空狀態的遞增結點r1,r2, …, rm,我們可以這樣構建這一空序列:
check[ri]=−ri+1 (1 i m−1),
check[rm]=−(da_size+1)
其中r1= e_head,為第乙個空值狀態對應的索引點。這樣我們在確定base值時只需掃瞄這一串行即可。這樣就省去了對非空狀態的訪問時間。
這種方法在空狀態並不太多的情況下可以很大程度的提高插入速度。
2, 刪除優化
1) 無用結點
對於刪除葉結點時產生的無用結點,可以通過依次判斷將它們置為空,使得可在插入新詞時得以重用。例如,如果我們刪除了上例中的"阿根廷",可以看到"阿根"這一狀態沒有子狀態,因此也可將它置為空。而"阿"這一狀態不能置空,因為它還有兩個子狀態。
2) 陣列長度的壓縮
在刪除了乙個狀態後,陣列末尾可能出現的連續空狀態我們是可以直接刪除的。另外我們還可以重新為最大非空索引點的狀態重新確定base值,因為它有可能已經由於刪除的進行而變小。這們我們可能又得以刪除一些空值狀態。
雙陣列trie樹
雙陣列trie double arraytrie 是trie樹的乙個簡單而有效的實現,由兩個整數陣列構成,乙個是base,另乙個是check。設陣列下標為i,如果base i check i 均為0,表示該位置為空。如果base i 為負值,表示該狀態為詞語。check i 表示該狀態的前一狀態,t...
雙陣列trie樹
原文 trie樹的陣列實現原理 trie retrieval tree 又稱字首樹,可以用來儲存多個字串,並且非常便於查詢。在trie中查詢乙個字串的時間只取決於組成該串的字元數,與樹的節點數無關。因此,它的查詢速度通常比二叉搜尋樹更快。trie的結構很簡單,每條邊表示乙個字元,從根節點到葉節點就可...
雙陣列TRIE樹原理
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!原貼 雙陣列trie樹原理 摘要 本文介紹了一種新的內部 內部排序的內部,也就是在記憶體裡 陣列結構的digital search演算法,叫做雙陣列,結合了陣列訪問的快速和鏈式儲存的壓縮。digital search樹的每一條弧在雙陣列中都可以以o...