当我们在百度上搜索的时候,在输入框里输入字符,会启发式得通知我们可能输入什么。当在翻译软件里面输入单词的时候,也会启发得显示出以我们的输入为前缀的单词。这其实用的就是字典树,或者他的变种。
字典树用于保存字符串,节省空间。他把多个字符串相同的前缀只保存依次,并且提高查询效率。多用于查询给定前缀的所有字符,或者查询给定前缀的所有字符数量。
字典树的节点代码:
/** * TrieNode: 字典树节点 * * @author xuejupo [email protected] * create in 2015-11-27 下午5:41:07 * */ class TrieNode { private int num;// 该节点有多少孩子 private TrieNode[] son = null; // 所有的孩子节点 private boolean isEnd; // 是否结束节点 private char val; // 节点的值 TrieNode() { num = 0; son = new TrieNode[SIZE]; isEnd = false; } }
插入节点:
/** * insert: 字典树的插入 * * @param str * void 返回类型 */ 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; }
统计以pre为前缀的单词个数
/** * countPre: 计算以pre为前缀的单词的个数 * * @param pre * @return int 返回类型 */ public int countPre(String pre) { if (pre == null || pre.length() == 0) { return -1; } TrieNode node = root; char[] letters = pre.toCharArray(); for (int i = 0, len = pre.length(); i < len; i++) { int pos = letters[i] - 'a'; if (node.son[pos] == null) { return 0; } else { node = node.son[pos]; } } return node.num; }
获取以pre为前缀的所有单词
/** * getPre: 获取pre前缀的树中所有的String的list * @param pre * @return * List<String> 返回类型 */ public List<String> getPre(String pre) { if (pre == null || pre.length() == 0) { return Collections.emptyList(); } List<String> result = new ArrayList<String>(); TrieNode node = root; char[] letters = pre.toCharArray(); for (int i = 0, len = pre.length(); i < len; i++) { int pos = letters[i] - 'a'; if (node.son[pos] == null) { return Collections.emptyList(); } else { node = node.son[pos]; } } StringBuilder sb = new StringBuilder(pre); getPre(node,sb,result); return result; }
/** * getPre: 将树里面以pre为前缀的所有String放入list * @param node * @param sb * @param l * void 返回类型 */ public void getPre(TrieNode node,StringBuilder sb,List<String> l){ if(node != null && node.isEnd){ l.add(sb.toString()); } if(node!=null){ for(TrieNode child: node.son){ if(child != null){ sb.append(child.val); getPre(child,sb,l); } } } }
查询单词是否在字典树中:
/** * isExist: 查询str是否在字典树中 * * @param str * @return boolean 返回类型 */ public boolean isExist(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 static void main(String[] s){ Trie t = new Trie(); t.insert("abc"); t.insert("abcasd"); t.insert("abca"); t.insert("a"); t.insert("asda"); t.insert("ba"); System.out.println("测试des是否存在字典树中:"+t.isExist("des")); System.out.println("插入des"); t.insert("des"); System.out.println("测试des是否存在字典树中:"+t.isExist("des")); System.out.println("输出以ab为前缀的所有单词:"); for(String p:t.getPre("ab")){ System.out.print(p+"\t"); } }
测试结果:
测试des是否存在字典树中:false 插入des 测试des是否存在字典树中:true 输出以ab为前缀的所有单词: abc abca abcasd