字典樹(Trie樹)

一:什麼是Trie樹面試

  Trie樹,即字典樹,又稱單詞查找樹或鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計和排序大量的字符串(但不只限於字符串),因此常常被搜索引擎系統用於文本詞頻統計。它的優勢是:最大限度地減小無謂的字符串比較,查詢效率比哈希表高。
  Trie的核心思想是空間換時間。利用字符串的公共前綴來下降查詢時間的開銷以達到提升效率的目的。算法

它有3個基本性質:
  1. 根節點不包含字符,除根節點外每個節點都只包含一個字符。
  2. 從根節點到某一節點,路徑上通過的字符鏈接起來,爲該節點對應的字符串。
  3. 每一個節點的全部子節點包含的字符都不相同。數組

二:樹的構建

舉個在網上流傳頗廣的例子,以下:
  題目:給你100000個長度不超過10的單詞。對於每個單詞,咱們要判斷他出沒出現過,若是出現了,求第一次出如今第幾個位置。
  分析:這題固然能夠用hash來解決,可是本文重點介紹的是trie樹,由於在某些方面它的用途更大。好比說對於某一個單詞,咱們要詢問它的前綴是否出現過。這樣hash就很差搞了,而用trie仍是很簡單。
  如今回到例子中,若是咱們用最傻的方法,對於每個單詞,咱們都要去查找它前面的單詞中是否有它。那麼這個算法的複雜度就是O(n2 )。顯然對於100000的範圍難以接受。如今咱們換個思路想。假設我要查詢的單詞是abcd,那麼在他前面的單詞中,以b,c,d,f之類開頭的我顯然沒必要考慮。而只要找以a開頭的中是否存在abcd就能夠了。一樣的,在以a開頭中的單詞中,咱們只要考慮以b做爲第二個字母的,一次次縮小範圍和提升針對性,這樣一個樹的模型就漸漸清晰了。
  比如假設有b,abc,abd,bcd,abcd,efg,hii 這6個單詞,咱們構建的樹就是以下圖這樣的:

  當時第一次看到這幅圖的時候,便立馬感到此樹之不凡構造了。單單從上幅圖即可窺知一二,比如大海搜人,立馬就能肯定東南西北中的到底哪一個方位,如此迅速縮小查找的範圍和提升查找的針對性,不失爲一創舉。
  ok,如上圖所示,對於每個節點,從根遍歷到他的過程就是一個單詞,若是這個節點被標記爲紅色,就表示這個單詞存在,不然不存在。
  那麼,對於一個單詞,我只要順着他從根走到對應的節點,再看這個節點是否被標記爲紅色就能夠知道它是否出現過了。把這個節點標記爲紅色,就至關於插入了這個單詞。
  這樣一來咱們查詢和插入能夠一塊兒完成,所用時間僅僅爲單詞長度,在這一個樣例,即是10。
  咱們能夠看到,trie樹每一層的節點數是26i 級別的。因此爲了節省空間。咱們用動態鏈表,或者用數組來模擬動態。空間的花費,不會超過單詞數×單詞長度。數據結構

三:前綴查詢

  已知n個由小寫字母構成的平均長度爲10的單詞,判斷其中是否存在某個串爲另外一個串的前綴子串。下面對比3種方法:
  最容易想到的:即從字符串集中從頭日後搜,看每一個字符串是否爲字符串集中某個字符串的前綴,複雜度爲O(n2)。
  使用hash:咱們用hash存下全部字符串的全部的前綴子串,創建存有子串hash的複雜度爲O(n*len),而查詢的複雜度爲O(n)* O(1)= O(n)
  使用trie:由於當查詢如字符串abc是否爲某個字符串的前綴時,顯然以b,c,d....等不是以a開頭的字符串就不用查找了。因此創建trie的複雜度爲O(n*len),而創建+查詢在trie中是能夠同時執行的,創建的過程也就能夠成爲查詢的過程,hash就不能實現這個功能。因此總的複雜度爲O(n*len),實際查詢的複雜度也只是O(len)。(說白了,就是Trie樹的平均高度h爲len,因此Trie樹的查詢複雜度爲O(h)=O(len)。比如一棵二叉平衡樹的高度爲logN,則其查詢,插入的平均時間複雜度亦爲O(logN))。搜索引擎

四:查詢

  Trie樹是簡單但實用的數據結構,一般用於實現字典查詢。咱們作即時響應用戶輸入的AJAX搜索框時,就是Trie開始。本質上,Trie是一顆存儲多個字符串的樹。相鄰節點間的邊表明一個字符,這樣樹的每條分支表明一則子串,而樹的葉節點則表明完整的字符串。和普通樹不一樣的地方是,相同的字符串前綴共享同一條分支。下面,再舉一個例子。給出一組單詞,inn, int, at, age, adv, ant, 咱們能夠獲得下面的Trie:
spa

能夠看出:
  每條邊對應一個字母。
  每一個節點對應一項前綴。葉節點對應最長前綴,即單詞自己。
  單詞inn與單詞int有共同的前綴「in」, 所以他們共享左邊的一條分支,root->i->in。同理,ate, age, adv, 和ant共享前綴"a",因此他們共享從根節點到節點"a"的邊。.net

  查詢操縱很是簡單。好比要查找int,順着路徑i -> in -> int就找到了。code

  搭建Trie的基本算法也很簡單,無非是逐一把每則單詞的每一個字母插入Trie。插入前先看前綴是否存在。若是存在,就共享,不然建立對應的節點和邊。好比要插入單詞add,就有下面幾步:
  1. 考察前綴"a",發現邊a已經存在。因而順着邊a走到節點a。
  2. 考察剩下的字符串"dd"的前綴"d",發現從節點a出發,已經有邊d存在。因而順着邊d走到節點ad
  3. 考察最後一個字符"d",這下從節點ad出發沒有邊d了,因而建立節點ad的子節點add,並把邊ad->add標記爲d。blog

五:Trie樹的應用

第一:詞頻統計;第二: 前綴匹配;第三:去重排序

  適用範圍:數據量大,重複多,可是數據種類小能夠放入內存
  基本原理及要點:實現方式,節點孩子的表示方式
  擴展:壓縮實現。

面試題有:有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。

    解答思路:http://www.javashuo.com/article/p-usjdmbqs-o.html

相關文章
相關標籤/搜索