Trie树,又称单词查找树,是一种树形结构。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比较高。
基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
核心思想:空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
缺点:基于空间换时间的思想,所以系统中若存在大量的没有公共前缀的字符串则会消耗大量内存。
应用场景:第一:词频统计;第二: 前缀匹配;第三:去重
实现代码:
1 /** 2 * Created by hardaway on 2018/7/26. 3 */ 4 public class Trie 5 { 6 private int SIZE=26; 7 private TrieNode root;//字典树的根 8 9 Trie() //初始化字典树 10 { 11 root=new TrieNode(); 12 } 13 14 private class TrieNode //字典树节点 15 { 16 private int num;//有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数 17 private TrieNode[] son;//所有的儿子节点 18 private boolean isEnd;//是不是最后一个节点 19 private char val;//节点的值 20 private int count; ///有多少单词以此字符结尾 21 22 TrieNode() 23 { 24 num=1; 25 son=new TrieNode[SIZE]; 26 isEnd=false; 27 } 28 } 29 30 //建立字典树 31 public void insert(String str) //在字典树中插入一个单词 32 { 33 if(str==null||str.length()==0) 34 { 35 return; 36 } 37 TrieNode node=root; 38 char[] letters=str.toCharArray(); 39 for(int i=0,len=str.length(); i<len; i++) 40 { 41 int pos=letters[i]-'a'; 42 if(node.son[pos]==null) 43 { 44 node.son[pos]=new TrieNode(); 45 node.son[pos].val=letters[i]; 46 } 47 else 48 { 49 node.son[pos].num++; 50 } 51 node=node.son[pos]; 52 } 53 node.isEnd=true; 54 node.count=node.count+1; 55 } 56 57 //计算单词前缀的数量 58 public int countPrefix(String prefix) 59 { 60 if(prefix==null||prefix.length()==0) 61 { 62 return-1; 63 } 64 TrieNode node=root; 65 char[]letters=prefix.toCharArray(); 66 for(int i=0,len=prefix.length(); i<len; i++) 67 { 68 int pos=letters[i]-'a'; 69 if(node.son[pos]==null) 70 { 71 return 0; 72 } 73 else 74 { 75 node=node.son[pos]; 76 } 77 } 78 return node.num; 79 } 80 //打印指定前缀的单词 81 public List<String> hasPrefix(String prefix) 82 { 83 if (prefix == null || prefix.length() == 0) 84 { 85 return null; 86 } 87 TrieNode node = root; 88 char[] letters = prefix.toCharArray(); 89 for (int i = 0, len = prefix.length(); i < len; i++) 90 { 91 int pos = letters[i] - 'a'; 92 if (node.son[pos] == null) 93 { 94 return null; 95 } 96 else 97 { 98 node = node.son[pos]; 99 } 100 } 101 List<String> list = new LinkedList<String>(); 102 preTraverse(node, prefix,list); 103 return list; 104 } 105 // 遍历经过此节点的单词. 106 public void preTraverse(TrieNode node, String prefix, List<String> list) { 107 if(node.isEnd) 108 list.add(prefix); 109 for (TrieNode child : node.son) { 110 if (child != null) { 111 preTraverse(child, prefix + child.val, list); 112 } 113 } 114 } 115 116 //在字典树中查找一个完全匹配的单词. 117 public boolean has(String str) 118 { 119 if(str==null||str.length()==0) 120 { 121 return false; 122 } 123 TrieNode node=root; 124 char[]letters=str.toCharArray(); 125 for(int i=0,len=str.length(); i<len; i++) 126 { 127 int pos=letters[i]-'a'; 128 if(node.son[pos]!=null) 129 { 130 node=node.son[pos]; 131 } 132 else 133 { 134 return false; 135 } 136 } 137 return node.isEnd; 138 } 139 140 //在字典树中查找一个完全匹配的单词的出现次数 141 public int count(String str) 142 { 143 if(str==null||str.length()==0) 144 { 145 return 0; 146 } 147 TrieNode node=root; 148 char[]letters=str.toCharArray(); 149 for(int i=0,len=str.length(); i<len; i++) 150 { 151 int pos=letters[i]-'a'; 152 if(node.son[pos]!=null) 153 { 154 node=node.son[pos]; 155 } 156 else 157 { 158 return 0; 159 } 160 } 161 if(node.isEnd) 162 return node.count; 163 return 0; 164 } 165 166 //前序遍历字典树. 167 public void preTraverse(TrieNode node) 168 { 169 if(node!=null) 170 { 171 System.out.print(node.val+"-"); 172 for(TrieNode child:node.son) 173 { 174 preTraverse(child); 175 } 176 } 177 } 178 179 public TrieNode getRoot() 180 { 181 return this.root; 182 } 183 184 public static void main(String[]args) 185 { 186 Trie tree=new Trie(); 187 String[]strs= {"banana","band","bee","absolute","acm","band"}; 188 String[]prefix= {"ba","b","band","abc",}; 189 for(String str:strs) 190 { 191 tree.insert(str); 192 } 193 System.out.println(tree.has("ban")); 194 System.out.println(tree.count("band")); 195 tree.preTraverse(tree.getRoot()); 196 System.out.println(); 197 for(String pre:prefix) 198 { 199 int num=tree.countPrefix(pre); 200 System.out.println(pre+": "+num); 201 } 202 System.out.println(tree.hasPrefix("ba")); 203 204 } 205 }
ref : https://www.cnblogs.com/yangjiannr/p/Trie-tree.html
https://blog.csdn.net/u012605629/article/details/40428987
http://acm.hdu.edu.cn/showproblem.php?pid=1251