为了准备阿里的网上笔试,这几天回顾了数据结构.看到字典树时,突然发现四六级的高频词可以用字典树找出来的.(应该不会是一个一个数出来的吧....)
构造字典树的过程如下:
1.首先确定树节点需要用怎么样的数据结构,我是这样写的:
public class TrieTreeNode {
/**
* 节点深度
*/
public short depth;
/**
* 存放当前节点的所有子节点
*/
public Map children = new HashMap();
/**
* 是否为单词的结尾
*/
public boolean isTail = false;
/**
* 双亲节点
*/
public TrieTreeNode parent;
/**
* 可以是a-z中的任意字母
*/
public char value;
/**
* 当单词相同时,wordCount++,用于计算相同的单词个数
*/
public int wordCount = 0;
/**
* 存放一整个单词
*/
public StringBuilder word = new StringBuilder();
public TrieTreeNode() {
// TODO Auto-generated constructor stub
}
}
2.通过节点的parent,children属性,来连接各个节点.每一个单词开始,都先将root节点作为双亲节点,单词中的每个字符来构造子节点,如果子节点存在,则直接使用,否则新建.
/**
* 通过给定的字符串创建字典树,暂时只支持英文
* @param data 用于创建字典树的字符串,暂不支持中文.
*/
public void createTrieTree(String data) {
//分解出文本中的单词
String[] strArr = data.split(regex);
//创建字典树的根节点
//将split后的str数组读入字典树
for (String s : strArr) {
//单个的字母不考虑
if (s.length() > 1) {
//每一个单词开始,都以root作为第一个双亲节点
TrieTreeNode parent = root;
//开始循环每一个单词
for (short i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//这里不考虑大写,只管小写.
c = (char) (c < 'a' ? c + 32 : c);
//创建子节点
TrieTreeNode child = createChildTrieTree(c, i, parent);
int key = child.value * 100 + child.depth;
parent.children.put(key, child);
parent = child;
}
//每一个单词循环完毕后,都将最后一个节点作为尾部.
parent.isTail = true;
//每次遇到相同的单词都将wordCount++
int wordCount = ++parent.wordCount;
String word = parent.word.toString();
//将单词和单词出现次数放入map
wordCountMap.put(word, new WordAndCount(word, wordCount));
}
}
}
/**
* 用于创建子节点
* @param c
* @param depth
* @param parent
* @return
*/
private TrieTreeNode createChildTrieTree(char c, short depth,
TrieTreeNode parent) {
//通过双亲节点,数的深度和字符c,3个条件判断是否已经存在该子节点,如果存在,直接获取.
TrieTreeNode child = getChild(c, depth, parent);
//如果不存在,就新建一个,并将参数赋给它.
if (child == null) {
child = new TrieTreeNode();
child.depth = depth;
child.parent = parent;
child.value = c;
//用于保存单词,通过isTail的配合,可以省去搜索单词的过程.
child.word.append(parent.word).append(c);
}
return child;
}
/**
* 根据字符c,树深度和双亲节点,获取子节点.
* @param c
* @param depth
* @param parent
* @return
*/
private TrieTreeNode getChild(char c, short depth, TrieTreeNode parent) {
// TODO Auto-generated method stub
//每一个双亲节点,都有一个HashMap来存放所有的子节点,key是根据c和depth来合成的.
Map children = parent.children;
//因为字典树的深度不超过26,所以十位和个位让depth来存放,百位以上让字符c来存放,这就可以构成一个独一无二的key来对应一个value
int key = c * 100 + depth;
return children.get(key);
}
最后,附上测试代码和数据:
@Test
public void test() {
String filePath = "C:\\logs\\file.log";
File srcFile = new File(filePath);
System.out.println("File Size:" + srcFile.length() / 1024 / 1024.0
+ "M");
long end2, end, start = System.nanoTime();
TrieTreeMaker maker = new TrieTreeMaker();
maker.setCapacity(1024 * 1024 * 20);
//通过给定的文件来构造字典树,也可通过调用createTrieTree("xxxx")实现.
maker.obtainDataAndCreateTrieTree(srcFile);
end = System.nanoTime();
System.out.println("After Creation is accomplished:"
+ BigDecimal.valueOf(end - start, 9));
//true代表升序,false代表降序
List> list = maker.sort(
maker.getWordCountMap(), true);
end2 = System.nanoTime();
System.out.println("After Sort is accomplished:"
+ BigDecimal.valueOf(end2 - end, 9));
//结果输出
for (Entry entry : list) {
System.out.println(entry.getValue().word + ":"
+ entry.getValue().count);
}
}
File Size:15.6962890625M
点击这里下载源码