单词查找树(Trie 树/字典树)可以用于存储大量的字符串以便支持快速模式匹配,主要应用在信息检索领域。
大作业要求分析几百万条密码结构,其中包含对英文单词及中文拼音密码的统计,经过查阅资料,发现Trie树能有效解决单词匹配问题。
问题解决分为两部分:Trie树生成;单词匹配
(1)Trie树生成
对于每一个节点,从根遍历到他的过程就是一个单词,如果这个节点被标记为红色,就表示这个单词存在,否则不存在。
那么,对于一个单词,我只要顺着他从根走到对应的节点,再看这个节点是否被标记为红色就可以知道它是否出现过了。把这个节点标记为红色,就相当于插入了这个单词。图示及参考代码如下:http://jaychang.iteye.com/blog/1559610
class Trie {
/** 定义单词查找树根节点,根节点为一个空的节点 */
private Vertex root = new Vertex();
/** 单词查找树的节点(内部类) */
private class Vertex {
/** 单词出现次数统计 */
int wordCount;
/** 以某个前缀开头的单词,它的出现次数 */
int prefixCount;
/** 当前节点的子节点用数组表示 */
Vertex[] vertexs = new Vertex[26];
/**
* 树节点的构造函数
*/
public Vertex() {
wordCount = 0;
prefixCount = 0;
}
}
/**
* 单词查找树构造函数
*/
public Trie() {
}
/**
* 向单词查找树添加一个新单词
*/
public void addWord(String word) {
addWord(root, word.toLowerCase()); //每次插入都从根节点开始找,单词转小写
}
/**
* 向单词查找树添加一个新单词
* @param root 单词查找树节点
* @param word 单词
*/
private void addWord(Vertex vertex, String word) {
if (word.length() == 0) {
} else if (word.length() > 0) {
vertex.prefixCount++; //此前缀出现次数+1
char c = word.charAt(0); //得到当前字符串的首字母
int index = c - 'a'; //将首字母与‘a’的ASCII码相减即可知道此首字母位于26位数组的哪一位
//如果当前结点的此子节点为空,说明从未出现过此前缀序列,则new一个结点代表这个位置有字母
if (null == vertex.vertexs[index])
{
vertex.vertexs[index] = new Vertex();
}
//从此子节点开始继续向下查找插入。word.substring(1)表示除去当前序列的第一个字母
addWord(vertex.vertexs[index], word.substring(1));
}
}
}
/**
* 测试片段
*/
public class Main {
public static void main(String[] args) {
Trie trie = new Trie(); //初始化一棵Trie树
trie.addWord("abc"); //插入单词
trie.addWord("abcd");
trie.addWord("abcde");
trie.addWord("abcdef");
}
}
(2)Trie树匹配
单词匹配即在已有的Trie树基础上检验单词是否存在于此字典中。如果有,返回至此为止出现次数。没有返回0.
/**
* 统计某个单词出现次数
* @param word 单词
* @return 出现次数
*/
public int countWord(String word) {
return countWord(root, word);
}
/**
* 每次都从根节点开始查找
*/
private int countWord(Vertex vertex, String word) {
if (word.length() == 0) { //一直查找到字符串结束,说明单词查找成功,返回
vertex.wordCount++;
return vertex.wordCount;
} else {
char c = word.charAt(0);
int index = c - 'a';
//如果字符串还未结束但当前结点无此子节点,说明这个序列不存在于字典中,单词查找失败
if (null == vertex.vertexs[index]) {
return 0;
} else {
return countWord(vertex.vertexs[index], word.substring(1));
}
}
}
特别的:
/**
* 统计字典中以某个前缀开始的单词,它的出现次数(前缀本身不算在内)
* @param word 前缀
* @return 出现次数
*/
public int countPrefix(String word) {
return countPrefix(root, word);
}
private int countPrefix(Vertex vertex, String prefixSegment) {
if (prefixSegment.length() == 0) {
return vertex.prefixCount;
} else {
char c = prefixSegment.charAt(0);
int index = c - 'a';
if (null == vertex.vertexs[index]) {
return 0;
} else {
return countPrefix(vertex.vertexs[index], prefixSegment.substring(1));
}
}
}