Java实现字典树TrieTree

为了准备阿里的网上笔试,这几天回顾了数据结构.看到字典树时,突然发现四六级的高频词可以用字典树找出来的.(应该不会是一个一个数出来的吧....)

构造字典树的过程如下:

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);
	}

3.通过如上步骤,字典树已经构造完成了.接下来是读值了,通过maker.getWordCountMap(),即可获得所有单词和它的出现次数.如果想排序显示,可以调用TrieTreeMaker中的sort方法进行排序.如果还有更特别的需求,可以通过get方法获取root节点,对它进行递归遍历.


最后,附上测试代码和数据:

@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
After Creation is accomplished:2.812941204s
After Sort is accomplished:0.004912021s
org:113792
java:74116
at:71064
springframework:68740
security:62020
web:53172
apache:37100
struts:33516
....由于结果太多,省略.

点击这里下载源码

你可能感兴趣的:(--1.1.Data,Structure)