http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html
trie,又稱前綴樹或字典樹,是一種有序樹
一:概念
下面我們有and,as,at,cn,com這些關鍵詞,那么如何構建trie樹呢?
從上面的圖中,我們或多或少的可以發現一些好玩的特性。
第一:根節點不包含字符,除根節點外的每一個子節點都包含一個字符。
第二:從根節點到某一節點,路徑上經過的字符連接起來,就是該節點對應的字符串。
第三:每個單詞的公共前綴作為一個字符節點保存。
二:使用范圍
既然學Trie樹,我們肯定要知道這玩意是用來干嘛的。
第一:詞頻統計。
可能有人要說了,詞頻統計簡單啊,一個hash或者一個堆就可以打完收工,但問題來了,如果內存有限呢?還能這么
玩嗎?所以這里我們就可以用trie樹來壓縮下空間,因為公共前綴都是用一個節點保存的。
第二: 前綴匹配
就拿上面的圖來說吧,如果我想獲取所有以"a"開頭的字符串,從圖中可以很明顯的看到是:and,as,at,如果不用trie樹,
你該怎么做呢?很顯然朴素的做法時間復雜度為O(N2) ,那么用Trie樹就不一樣了,它可以做到h,h為你檢索單詞的長度,
可以說這是秒殺的效果。
舉個例子:現有一個編號為1的字符串”and“,我們要插入到trie樹中,采用動態規划的思想,將編號”1“計入到每個途徑的節點中,
那么以后我們要找”a“,”an“,”and"為前綴的字符串的編號將會輕而易舉。
1 package jd; 2 3 import java.util.HashMap; 4 import java.util.Map.Entry; 5 6 /** 7 * 前綴樹的實現 8 * 9 * @author li 10 * 11 */ 12 public class TrieTree { 13 /** 14 * 前綴樹節點 15 * 16 * @author li 17 * 18 */ 19 private class Node { 20 private int prefixNum;// 以該字符串為前綴的字符數 21 private Node[] childNodes; 22 private boolean isLeaf;// 是否為單詞 23 private int dumplicateNum;// 該字符串重復出現的次數(單詞重復出現次數) 24 25 public Node() { 26 prefixNum = 0; 27 isLeaf = false; 28 dumplicateNum = 0; 29 childNodes = new Node[26]; 30 } 31 } 32 33 private Node root;// 根節點 34 35 public TrieTree() { 36 root = new Node(); 37 38 } 39 40 public void insert(String word) { 41 insert(this.root, word); 42 } 43 44 /** 45 * 插入單詞 46 * 47 * @param root 48 * @param word 49 */ 50 public void insert(Node root, String word) { 51 word = word.toLowerCase(); 52 char[] chars = word.toCharArray(); 53 for (int i = 0; i < chars.length; i++) { 54 int index = chars[i] - 'a';// 對應的下標 55 if (root.childNodes[index] == null) { 56 root.childNodes[index] = new Node(); 57 } 58 root.childNodes[index].prefixNum++; 59 if (i == chars.length - 1) { 60 root.childNodes[index].isLeaf = true;// 單詞 61 root.childNodes[index].dumplicateNum++;// 該單詞出現的個數 62 } 63 root = root.childNodes[index]; 64 } 65 66 } 67 68 /** 69 * 獲取以prefixStr為前綴的字符串 70 * 71 * @param prefixStr 72 * @return 73 */ 74 private HashMap<String, Integer> getPrefixString(Node root, String prefixStr) { 75 prefixStr = prefixStr.toLowerCase(); 76 HashMap<String, Integer> map = new HashMap<String, Integer>(); 77 char[] prefixs = prefixStr.toCharArray(); 78 for (int i = 0; i < prefixs.length; i++) { 79 int index = prefixs[i] - 'a'; 80 if (root.childNodes[index] != null) { 81 root = root.childNodes[index]; 82 } else { 83 return null; 84 } 85 } 86 dfs(root, prefixStr, map); 87 return map; 88 89 } 90 91 public HashMap<String, Integer> getPrefixString(String prefixStr) { 92 93 return getPrefixString(root, prefixStr); 94 95 } 96 97 /** 98 * 深度優先遍歷 99 * 100 * @param root 101 * :根節點 102 * @param map 103 */ 104 private HashMap<String, Integer> dfs(Node root, String prefixs, 105 HashMap<String, Integer> map) { 106 if (root == null) { 107 return null; 108 } 109 if (root.isLeaf) { 110 map.put(prefixs, root.dumplicateNum); 111 112 } 113 Node[] nodes = root.childNodes; 114 for (int i = 0; i < nodes.length; i++) { 115 if (nodes[i] != null) { 116 char c = (char) (i + 'a'); 117 String tempStr = prefixs + c; 118 dfs(root.childNodes[i], tempStr, map); 119 } 120 121 } 122 return map; 123 124 } 125 126 /** 127 * 遍歷所有的字符串 128 * 129 * @return 130 */ 131 public HashMap<String, Integer> dfs() { 132 HashMap<String, Integer> map = new HashMap<String, Integer>(); 133 return dfs(this.root, "", map); 134 } 135 136 /** 137 * 刪除節點 138 * 139 * @param word 140 */ 141 public boolean delete(String word) { 142 // 不存在該單詞則刪除 143 Node p = root; 144 if (!searchWord(word)) { 145 return false; 146 } 147 word = word.toLowerCase(); 148 char[] chars = word.toCharArray(); 149 for (int i = 0; i < chars.length; i++) { 150 int index = chars[i] - 'a'; 151 Node[] nodes = p.childNodes; 152 if (nodes[index].prefixNum == 1) { 153 nodes[index] = null; 154 return true; 155 } else { 156 nodes[index].prefixNum--; 157 p = nodes[index]; 158 } 159 } 160 if (p.dumplicateNum == 1) { 161 p.isLeaf = false; 162 } 163 p.dumplicateNum--; 164 return true; 165 166 } 167 168 /** 169 * 170 * @param root 171 * @param word 172 * @return 173 */ 174 private boolean searchWord(Node root, String word) { 175 if (root == null) { 176 return false; 177 } 178 word = word.toLowerCase(); 179 char[] chars = word.toCharArray(); 180 181 for (int i = 0; i < chars.length; i++) { 182 int index = chars[i] - 'a'; 183 Node[] nodes = root.childNodes; 184 if (nodes[index] != null) { 185 root = nodes[index]; 186 } else { 187 return false; 188 } 189 190 } 191 if (!root.isLeaf) { 192 return false; 193 } 194 return true; 195 196 } 197 198 public boolean searchWord(String word) { 199 return searchWord(root, word); 200 201 } 202 203 public static void main(String[] args) { 204 TrieTree trieTree = new TrieTree(); 205 trieTree.insert("gab"); 206 trieTree.insert("gb"); 207 trieTree.insert("gb"); 208 trieTree.insert("gbc"); 209 trieTree.insert("gbc"); 210 trieTree.insert("go"); 211 trieTree.insert("go"); 212 trieTree.insert("goa"); 213 for (Entry<String, Integer> entry : trieTree.getPrefixString("g") 214 .entrySet()) { 215 System.out.println(entry.getKey() + " : " + entry.getValue()); 216 217 } 218 trieTree.delete("gb"); 219 System.out.println("***************"); 220 for (Entry<String, Integer> entry : trieTree.getPrefixString("g") 221 .entrySet()) { 222 System.out.println(entry.getKey() + " : " + entry.getValue()); 223 224 } 225 226 } 227 }