頗有段時間沒寫此係列了,今天咱們來講Trie樹,Trie樹的名字有不少,好比字典樹,前綴樹等等。node
一:概念this
下面咱們有and,as,at,cn,com這些關鍵詞,那麼如何構建trie樹呢?spa
從上面的圖中,咱們或多或少的能夠發現一些好玩的特性。code
第一:根節點不包含字符,除根節點外的每個子節點都包含一個字符。orm
第二:從根節點到某一節點,路徑上通過的字符鏈接起來,就是該節點對應的字符串。內存
第三:每一個單詞的公共前綴做爲一個字符節點保存。字符串
二:使用範圍get
既然學Trie樹,咱們確定要知道這玩意是用來幹嗎的。hash
第一:詞頻統計。it
可能有人要說了,詞頻統計簡單啊,一個hash或者一個堆就能夠打完收工,但問題來了,若是內存有限呢?還能這麼
玩嗎?因此這裏咱們就能夠用trie樹來壓縮下空間,由於公共前綴都是用一個節點保存的。
第二: 前綴匹配
就拿上面的圖來講吧,若是我想獲取全部以"a"開頭的字符串,從圖中能夠很明顯的看到是:and,as,at,若是不用trie樹,
你該怎麼作呢?很顯然樸素的作法時間複雜度爲O(N2) ,那麼用Trie樹就不同了,它能夠作到h,h爲你檢索單詞的長度,
能夠說這是秒殺的效果。
舉個例子:現有一個編號爲1的字符串」and「,咱們要插入到trie樹中,採用動態規劃的思想,將編號」1「計入到每一個途徑的節點中,
那麼之後咱們要找」a「,」an「,」and"爲前綴的字符串的編號將會垂手可得。
三:實際操做
到如今爲止,我想你們已經對trie樹有了大概的掌握,下面咱們看看如何來實現。
package Algorithm; public class Trie { private int SIZE=26; private TrieNode root;//字典樹的根 Trie(){//初始化字典樹 root=new TrieNode(); } private class TrieNode{//字典樹節點 private int num;//有多少單詞經過這個節點,即節點字符出現的次數 private TrieNode[] son;//全部的兒子節點 private boolean isEnd;//是否是最後一個節點 private char val;//節點的值 TrieNode(){ num=1; son=new TrieNode[SIZE]; isEnd=false; } } //創建字典樹 public void insert(String str){//在字典樹中插入一個單詞 if(str==null||str.length()==0){ return; } TrieNode node=root; char[]letters=str.toCharArray(); for(int i=0,len=str.length();i<len;i++){ int pos=letters[i]-'a'; if(node.son[pos]==null){ node.son[pos]=new TrieNode(); node.son[pos].val=letters[i]; }else{ node.son[pos].num++; } node=node.son[pos]; } node.isEnd=true; } //計算單詞前綴的數量 public int countPrefix(String prefix){ if(prefix==null||prefix.length()==0){ return-1; } TrieNode node=root; char[]letters=prefix.toCharArray(); for(int i=0,len=prefix.length();i<len;i++){ int pos=letters[i]-'a'; if(node.son[pos]==null){ return 0; } else{ node=node.son[pos]; } } return node.num; } //在字典樹中查找一個徹底匹配的單詞. public boolean has(String str){ if(str==null||str.length()==0){ return false; } TrieNode node=root; char[]letters=str.toCharArray(); for(int i=0,len=str.length();i<len;i++){ int pos=letters[i]-'a'; if(node.son[pos]!=null){ node=node.son[pos]; }else{ return false; } } return node.isEnd; } //前序遍歷字典樹. public void preTraverse(TrieNode node){ if(node!=null){ System.out.print(node.val+"-"); for(TrieNode child:node.son){ preTraverse(child); } } } public TrieNode getRoot(){ return this.root; } public static void main(String[]args){ Trie tree=new Trie(); String[]strs={"banana","band","bee","absolute","acm",}; String[]prefix={"ba","b","band","abc",}; for(String str:strs){ tree.insert(str); } System.out.println(tree.has("abc")); tree.preTraverse(tree.getRoot()); System.out.println(); //tree.printAllWords(); for(String pre:prefix){ int num=tree.countPrefix(pre); System.out.println(pre+""+num); } } }