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 }