以前說搜索提示的時候留了一個尾巴,就是Trie樹的結構沒有說,這一篇簡單的說一下Trie樹的實現方式。golang
在計算機科學中,trie,又稱前綴樹或字典樹,是一種有序樹,用於保存關聯數組,其中的鍵一般是字符串。與二叉查找樹不一樣,鍵不是直接保存在節點中,而是由節點在樹中的位置決定。一個節點的全部子孫都有相同的前綴,也就是這個節點對應的字符串,而根節點對應空字符串。通常狀況下,不是全部的節點都有對應的值,只有葉子節點和部份內部節點所對應的鍵纔有相關的值。算法
Trie這個術語來自於retrieval。根據詞源學,trie的發明者Edward Fredkin把它讀做英語發音:/ˈtriː/ "tree"。可是,其餘做者把它讀做英語發音:/ˈtraɪ/ "try"編程
以上文字來自維基百科
數組
搜索提示和分詞是Trie樹的經典應用範圍。微信
Trie樹
又叫字典樹
,本質上是一個多叉樹,每個節點就是一個多叉的結構,若是是英文的匹配,那麼是一個26叉樹
,每一個節點一個26長度的數組,每一個節點的數據結構以下數據結構
type TrieNode struct{ flag bool hasNext bool nexts [26]*TrieNode }
其中flag
保存的是當前節點是否已是一個完整字符串了。hasNext
表示是否還有下一節點,而Trie樹
畫出來就是下面這個樣子。

從畫出來的圖,很直觀的能夠看出來這棵樹的構造方法和遍歷方法,由於每一個節點都有一個26長度的數組,來了一個字符,經過字符的編號直接就能夠遍歷到下一個節點,查找的時候複雜度就是O(K),K表示查找的字符串長度,這種數據結構簡單明瞭,實現起來也很容易。優化
可是這種數據結構有個問題,就是內存使用得太多了,若是是中文查找的話,須要把全部的中國字都編號到這個數組中,因而有一種優化方法,就是把數組變成變長的,若是按照二分進行每一個節點的查找的話,每一個節點的查找時間變成了O(lg(n)),總體的查找時間變成K*O(log(n)),n是數組平均長度,這樣的話,雖然內存的使用上下降了,可是查找速度變長了,並且插入的時候須要按序插入,插入也變慢了。spa
按照真正的樹形結構,經過各類指針滿天飛的形式來實現Trie樹,基本上屬於新人練手的水平,純粹爲了瞭解這個數據結構或者大學生作作課程設計,工程化的可能性幾乎爲0。設計
正由於有上面的各類問題,因而有人發明了雙數組Trie樹
,其實在這個以前還有個三數組的Trie樹
,爲了避免讓你們更迷糊,咱們直接來看看雙數組的形式吧。指針
所謂雙數組Trie樹,固然就是經過兩個數組來實現這棵樹了,這兩個數組分別叫base數組
和check數組
,一個是基礎數組,一個是檢查數組,Suggestion服務對Trie樹的操做基本上須要有Trie樹的構建部分和Trie樹的查詢部分,插入操做基本上不多使用,後面說數據部分的時候會說到爲何。
Trie樹其實是一種有限狀態機
,經過狀態轉移矩陣
在各個狀態之間跳轉,咱們不去管這兩個概念,會越說越糊塗,若是你實在是有興趣,能夠本身去查查資料,咱們直接來點實在的,用一個例子來看看雙數組Trie樹
是如何工做的。
假設咱們的詞庫裏面有這麼一些詞,中國,北京,中華,華北
這幾個詞語,須要構建一棵Trie樹
,構建好了之後,用戶輸入中
字的時候,能把中國,中華
都給提示出來。這裏的例子直接使用了中文是爲了更直觀,免得都是abc這種例子容易亂,雖然最後的處理上中文有點特殊,但不影響算法描述。
首先,拿到這些個詞之後,第一步就要構建這棵Trie樹了,構建以前,咱們先對每個字進行編號一下,這個只是爲了方便描述,中:1,國:2,北:3,京:4,華:5
。
初始化空數組,初始化base和check兩個空數組。
base的元素爲1表示是開始位偏移爲1,爲-1表示沒有數據。
check的元素中,-1表示沒有數據,-2表示這是一個詞結束了,-3表示是根節點,以下圖所示

初始化好了之後就能夠開始依次讀取數據了,先讀取出中國
兩個字,開始插入
中國
這個詞中
字從根節點開始,base[0]=1,插入位置爲base[0]+中
,中
的編號爲1,因此是base[0]+1=1+1=2
,因而獲得應該插入到base[2]的位置。
檢查check[2]
,看到check[2]爲-1,表示base[2]沒有數據,能夠插入
更新check[2]
爲上一個節點的base數組的下標,上一個節點是base[0],因此更新爲0
更新base[2]
爲上一個節點的base數組的值,上一個節點base[0]的值是1,因此更新爲1
按照上面的四個步驟更新完了之後,整個數組變成下面這個樣子,對着圖能夠仔細想一想整個過程,而後你想一想僞代碼是什麼樣子的,再想一想如何編程。

國
字因爲這個詞語還沒結束,因此從base[2]開始繼續往下走
從base[2]開始,插入位置爲base[2]+國
,國
的編號是2,因此是base[2]+2=1+2=3
,應該插入到base[3]中
檢查check[3]
,看到check[2]爲-1,表示base[2]沒有數據,能夠插入
更新check[3]
爲上一個節點的base數組下標,上一個是base[2],因此更新爲2
更新base[3]
爲上一個節點的base數組的值,上一個節點base[2]的值是2,因此更新爲2

北京
和中華
這個詞你們能夠本身插入一下北京
和中華
這兩個詞,插入之後結構變成下面的樣子

華北
這個詞到如今一切都很順利,接下來插入華北
這個詞,這裏就會遇到衝突了,遇到衝突之後,進行以下算法
華
字這個插入正常,最後圖像以下所示

北
字因爲這個詞語還沒結束,因此從base[6]開始繼續往下走
從base[6]開始,插入位置爲base[2]+北
,國
的編號是3,因此是base[6]+3=1+3=4
,應該插入到base[4]中
檢查check[4]
,看到check[2]爲0,表示base[4]有數據,產生衝突!!
從base[6]日後找空閒空閒,空閒空間的概念是兩個數組都是-1
找到base[8]和check[8]爲空閒空間
更新check[8]爲上一節點的值,6
計算base[6]應該的值爲8-北=8-3=5,更新base[6]爲5,解決衝突!!

要進行查詢,就相對簡單了,拿到一個詞,好比中華
,先肯定每一個字的編號,而後按照兩個數組去遍歷,就知道這個詞在不在這個Trie樹中了,複雜度就是詞的長度。
本篇比較簡單,就是上次搜索提示的一個小尾巴,後面會說到分詞的技術,這個Trie樹也是分詞的主要數據結構。
若是你以爲不錯,歡迎轉發給更多人看到,也歡迎關注個人公衆號,主要聊聊搜索,推薦,廣告技術,還有瞎扯。。文章會在這裏首先發出來:)掃描或者搜索微信號XJJ267或者搜索西加加語言就行