樹結構不管是組織數據,仍是行使特定功能都是一種強大的武器,今天咱們來詳細解讀一下字典樹。git
字典樹:github
字典樹是一種特殊的搜索樹,能夠用來統計字符串數量,統計前綴詞頻。數據結構
字典樹有如下基本性質:併發
1.有一個根節點,但根節點無數據。函數
2.每一個節點有N個出度(N爲組成字符串的字符的類型數目),即字典樹是個N叉樹spa
3.節點中有判斷是否爲單詞的bool型標誌位。code
其實字典樹能夠根據咱們的須要進行適當的變更我本身嘗試些了一個字典樹的類,在此分享一下:blog
基本數據結構組成:字符串
1 package com.choi.tools; 2
3 public class TrieTree { 4
5 final Trie root = new Trie(); 6
7 class Trie { 8 int preCount = 0; 9 int wordCount = 0; 10 Trie[] next = new Trie[52]; 11
12 public Trie() { 13 for (int i = 0; i < 52; i++) { 14 next[i] = null; 15 } 16 } 17 } 18 }
這裏我作了沒有使用bool型變量來肯定到某一節點的路徑是否組成單詞,而是利用wordCount來計數該節點組成的單詞數目,用preCount來記錄root到該節點的路徑做爲prefix的get
數目。
相關函數:
1 synchronized public void insert(String s) throws NullPointerException { 2 if (s == null) { 3 throw new NullPointerException(); 4 } 5 int L = s.length(); 6 Trie p = root; 7 for (int i = 0; i < L; i++) { 8 int id = getId(s.charAt(i)); 9 if(id==-1){ 10 System.out.println("插入s時,有非法字符"); 11 return ; 12 } 13 if (p.next[id] == null) { 14 p.next[id] = new Trie(); 15 } 16 p.next[id].preCount++; 17 p = p.next[id]; 18 } 19 p.wordCount++; 20 } 21
22 public int findWord(String s){ 23 if(s==null){ 24 return 0; 25 } 26 int L = s.length(); 27 Trie p = root; 28 int i = 0; 29 for(; i<L; i++){ 30 int id = getId(s.charAt(i)); 31 if(id==-1){ 32 return 0; 33 } 34 if(p.next[id]==null) 35 break; 36 p = p.next[id]; 37 } 38 if(i==L) 39 return p.wordCount; 40 else
41 return 0; 42 } 43
44 public int findPrefix(String s){ 45 if(s==null){ 46 return 0; 47 } 48 int L = s.length(); 49 Trie p = root; 50 int i = 0; 51 for(; i<L; i++){ 52 int id = getId(s.charAt(i)); 53 if(p.next[id]==null) 54 break; 55 p = p.next[id]; 56 } 57 return p.preCount; 58 } 59
60 private int getId(char c){ 61 int id = 0; 62 if (c <= 'z' && c >= 'a') { 63 id = c - 'a'; 64 } else if (c <= 'Z' && c >= 'A') { 65 id = c - 'A'; 66 } else { 67 return -1; 68 } 69 return id; 70 }
insert函數我用synchronized修飾,使該字典樹可以支持併發操做。提供了兩個得到統計量的函數:findPrefix,findWord 分別統計前綴的數目,和單詞的數量。
對異常的處理:
插入操做:若是參數爲null,則拋出NullPointerException;若是插入字符串包含非法字符,則跳過不插入,並輸出錯誤信息到控制檯。
find操做:若是參數爲空和遇到包含非法字符的字符串都範圍0,表示沒有找到。
操做分析:
根據字典樹數據結構的特徵,可知插入一個字符串的時間複雜度爲O(k)(k爲字符串的長度),空間複雜度以這段代碼爲例每一個節點大概佔52*4+4+4個字節,一個字符佔一個字節,也就是說當咱們存儲一個字符串時的空間大小爲k*216個字節空間,這個量仍是很大的,可是若是考慮到字典樹的應用場景,這偏偏是其優勢了,字典樹一般用於大量的文本單詞統計,當一個單詞反覆出現超過216次時,那麼多出的次數就好似不佔空間了,而對於一部小說而言216是很容易超過的數字。
其實當數據量小的時候咱們能夠考慮HashTable,key存字符串,value存字符串出現的數目。其實hashtable的時間效率並不比字典樹好,反而會稍稍慢一些,由於對每一個字符串算hash值時的時間複雜度也是O(k),而且其還要處理hash衝突。
總之若是數據量小能夠考慮hashtabe,若是數據量大那字典樹是個很是不錯的選擇。
GitHub傳送門:https://github.com/choitony/TrieTree/tree/master 歡迎閱讀,指正!