Tire樹,即字典樹,又稱單詞查找樹或鍵樹,是一種樹型結構,是一種哈希樹的變種。典型的應用是統計和排序大量的字符串(不只限於字符串),因此常常被搜索引擎用於文本詞頻統計。它的優勢是:最大限度地減小無畏的比較,查詢效率比哈希表高。
Tire樹的核心思想是空間換時間。利用字符串的公共前綴來下降查詢時間的開銷以達到提升效率的目的。Tire樹的缺點是內存消耗很是大。算法
根節點不包含字符,每條邊表明一個字符。
從根節點到某一節點,路徑上通過的字符鏈接起來爲該節點對應的字符串。
每一個節點的全部子節點包含的字符都不相同數組
Trie樹的鍵不是直接保存在節點中,而是由節點在樹中的位置決定。一個節點的全部子孫都有相同的前綴(prefix),從根節點到當前結點的路徑上的全部字母組成當前位置的字符串,結點能夠保存當前字符串、出現次數、指針數組(指向子樹)以及是不是結尾標誌等等。數據結構
public TrieNode{ int num;//單詞前綴出現的次數 TrieNode next[26];//指向子樹的節點 boolean exist;//標記是否此處構成單詞 }
/** @para tire:數字的行表明節點的插入順序值,列表明子節點(若是是小寫字母的話是26個) @para st:要插入的字符串 @para num:維持的一個節點序號 @para isword:判斷當前節點是否構成單詞 **/ public void Insert(String st,int index){ for(int i=0;i<st.length();i++){ int x = Integer.valueOf(st.charAt(i)-'a'); if(tire[index][x]==0) tire[num][x]=++num;//若是插入的字母在以前未出現則插入 index = tire[index][x];爲下一個字母的插入作準備 } isword[index]=true;標記該字母是不是單詞的尾節點 }
public boolean find(String st,int index){ for(int i=0;i<st.length();i++){ int x = Integer.valueOf(st.charAt(i)-'a'); if(tire[index][x]==0) return false; index = tire[index][x]; } return true; //查詢整個單詞時,return isword[index]; }
/** @para tire:數字的行表明節點的插入順序值,列表明子節點(若是是小寫字母的話是26個) @para st:要插入的字符串 @para num:維持的一個節點序號 @para isword:判斷當前節點是否構成單詞 @para sum:存儲當前節點的前綴被訪問的次數 **/ public void Insert(String st,int index){ for(int i=0;i<st.length();i++){ int x = Integer.valueOf(st.charAt(i)-'a'); if(tire[index][x]==0) tire[num][x]=++num;//若是插入的字母在以前未出現則插入 index = tire[index][x];爲下一個字母的插入作準備 sum[index]++; } isword[index]=true;標記該字母是不是單詞的尾節點 } public int search(){ root=0; len=strlen(s); for(int i=0;i<len;i++) { int id=s[i]-'a'; if(!trie[root][id]) return 0; root=trie[root][id]; }//root通過此循環後變成前綴最後一個字母所在位置的後一個位置 return sum[root];//由於前綴後移了一個保存,因此此時的sum[root]就是要求的前綴出現的次數 }
實現將已知的一些字符串(字典)的有關信息保存到trie樹裏,查找另一些未知字符串是否出現過或者出現頻率。舉例以下:
一、有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。數據結構和算法
二、給出N 個單詞組成的熟詞表,以及一篇全用小寫英文書寫的文章,請你按最先出現的順序寫出全部不在熟詞表中的生詞。搜索引擎
三、給出一個詞典,其中的單詞爲不良單詞。單詞均爲小寫字母。再給出一段文本,文本的每一行也由小寫字母構成。判斷文本中是否含有任何不良單詞。例如,若rob是不良單詞,那麼文本problem含有不良單詞。設計
四、1000萬字符串,其中有些是重複的,須要把重複的所有去掉,保留沒有重複的字符串。請怎麼設計和實現?指針
五、一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間複雜度分析。日誌
六、尋找熱門查詢:搜索引擎會經過日誌文件把用戶每次檢索使用的全部檢索串都記錄下來,每一個查詢串的長度爲1-255字節。假設目前有一千萬個記錄,這些查詢串的重複讀比較高,雖然總數是1千萬,可是若是去除重複和,不超過3百萬個。一個查詢串的重複度越高,說明查詢它的用戶越多,也就越熱門。請你統計最熱門的10個查詢串,要求使用的內存不能超過1G(京東筆試題簡答題與此相似)。code
trie樹利用多個字符串的公共前綴來節省存儲空間,反之,當咱們把大量字符串存儲到一棵trie樹上時,咱們能夠快速獲得某些字符串的公共前綴。舉例:blog
1) 給出N 個小寫英文字母串,以及Q 個詢問,即詢問某兩個串的最長公共前綴的長度是多少. 解決方案:
首先對全部的串創建其對應的字母樹。此時發現,對於兩個串的最長公共前綴的長度即它們所在結點的公共祖先個數,因而,問題就轉化爲了離線(Offline)的最近公共祖先(Least Common Ancestor,簡稱LCA)問題。
而最近公共祖先問題一樣是一個經典問題,能夠用下面幾種方法:
利用並查集(Disjoint Set),能夠採用經典的Tarjan 算法;
求出字母樹的歐拉序列(Euler Sequence )後,就能夠轉爲經典的最小值查詢(Range Minimum Query,簡稱RMQ)問題了;
Trie樹是一棵多叉樹,只要先序遍歷整棵樹,輸出相應的字符串即是按字典序排序的結果。
舉例:給你N 個互不相同的僅由一個單詞構成的英文名,讓你將它們按字典序從小到大排序輸出。
如後綴樹,AC自動機等。
優勢:
trie數據查找與不完美哈希表(鏈表實現)在最壞狀況下更快;對於trie樹,最差爲O(m),m爲查找字符串的長度;對於不完美哈希表,會有鍵值衝突(不一樣鍵哈希相同),最壞爲O(N),N爲所有字符產生的個數。典型狀況是O(m)用於哈希計算,O(1)用於數據查找。
trie中不一樣鍵沒有衝突
trie的桶與哈希表用於存儲鍵衝突的桶相似,僅在單個鍵與多個值關聯時須要
當更多的鍵加入到trie中,無需提供hash方法或改變hash方法
trie經過鍵爲條目提供字母順序
缺點:
trie數據查找在某些狀況下(磁盤或隨機訪問時間遠遠高於主存)比哈希錶慢
當鍵值爲某些類型(如浮點型),前綴鏈很長且前綴不是特別有意義。
一些trie會比hash表更消耗內存。對於trie,每一個字符串的每一個字符都要分配內存;對於大多數hash,只須要爲整個條目分配一塊內存。
二叉搜索樹,又稱二叉排序樹,它知足:
任意節點若是左子樹不爲空,左子樹全部節點的值都小於根節點的值;
任意節點若是右子樹不爲空,右子樹全部節點的值都大於根節點的值;
左右子樹也都是二叉搜索樹;
全部節點的值都不相同。
其實二叉搜索樹的優點已經在與查找、插入的時間複雜度上了,一般只有O(log n),不少集合都是經過它來實現的。在進行插入的時候,實質上是給樹添加新的葉子節點,避免了節點移動,搜索、插入和刪除的複雜度等於樹的高度,屬於O(log n),最壞狀況下整棵樹全部的節點都只有一個子節點,徹底變成一個線性表,複雜度是O(n)。
Trie樹在最壞狀況下查找要快過二叉搜索樹,若是搜索字符串長度用m來表示的話,它只有O(m),一般狀況(樹的節點個數要遠大於搜索字符串的長度)下要遠小於O(n)。