关闭

Java中文键树的一种实现(附带模糊查询功能)

标签: 数据结构中文键树模糊查询性能测试java
2800人阅读 评论(5) 收藏 举报
分类:

首先在文章的开头声明一下哈,本文只是介绍一种Java蛮力键树的实现,并没有什么高深的数据结构,所以数据量不超过百万字符的可以参考,数据量太大的另请高明吧。另外,后面的键树代码实际上不仅适用于中文存储和查找,只要是字符串形式的数据都可以存储。比如:“锄禾日当午”、“a+你好啊234#jfjf”这样形式的数据都可以放进去(韩文柬埔寨文怎么混搭都可以,只要编码方式别搞混)。

键树是一种非常简单的数据结构,相信学过的人都知道,没学过的人一看就明白:


图1 一棵键树

好了,既然它这么简单,那我就不介绍了,想要完整阅读键树定义的读者可以随便百度,下面开始设计分析。传统键树拥有两种可选的存储结构,分别是双链树和多重链表(又称为Trie树)。多重链表表示法适用于键树中结点的度较大的情况,因此本文在实现中文键树时考虑使用多重链表结构。键树将一条完整的信息串分割成一层一层的结点结构,对应到中文,即每一个结点上存储了一个汉字信息。典型的键树通常采用数组来实现结点后代的存储,这是源于英文字母只有固定26个。鉴于中文无法同样考虑,在实现时采用ArrayList来实现后代的存储。这既保证了查询速度,又避免数组越界的问题。因此,一个结点的结构就是:

class TrieNode {
	public String value;
	public ArrayList<TrieNode> ptr = null;
	public TrieNode(String value) {
		this.value=value;
		ptr =new ArrayList<TrieNode>();
	}
}
    向这棵键树中插入新节点是很简单的,比如插入的新内容是一个"apple"的单词,那么将这个字符串依次拆开,逐个在键树中向下寻找(在ArrayList中遍历比较),并最终决定放不放就行(放就add,不放就下一层或者结束),具体实现就是后文中的insert(String key)方法。同样的道理,查找也很简单。

    好了,键树就实现完成了,很简单。这里加了一个内容:因为正常人实现这种数据结构都会想要提供模糊查询的功能,比如我查找:"ap",就希望这棵树能给我一个"apple",满足你。实现这个功能的基本功就在于最简单的树的先序遍历,不过由于这是贱树,所以又不太一样。

    因为前面说了百万以上不要看本文,所以我这里的先序遍历用了递归(百万以下就不要叫会栈溢出,随心所欲的插就行)。原理很简单,往遍历方法里传一个树结点,比如前面查询了"ap",那么"ap"的p结点就传了进来。然后用一个StringBuffer来装进后面的"ple"。如果还有类似于"application"这样的单词,就倒回去,再在StringBuffer里装一遍"plication"。有多个关键词的就会将每次查询的StringBuffer装进一个ArrayList<String>,最后这个集合searchResult就存储了模糊查询的结果。

ArrayList<String> searchResult=new ArrayList<String>();
StringBuffer tempWord=new StringBuffer();
int start=0;
private void traverseTree(TrieNode p){
	if(!(p.ptr.isEmpty())){
		for(TrieNode tn:p.ptr){
			tempWord.append(tn.value);
			start++;
			traverseTree(tn);
			start--;
			tempWord.delete(start,tempWord.length());
		}
	}else{
		searchResult.add(tempWord.toString());
	}
}

    最后说一下性能和改进:

    性能:

        20万字符(约60000条古诗)模糊查询平均耗时为1毫秒。插入的时间非常短,短到我忘了测(以上性能什么概念呢,就是如果你要做一个简易的搜索提示框的话,后台用这个键树来实现是非常合适的,搜索提示的反应零卡顿非常快。那如果是点一个按钮然后查询那种功能就更不在话下了)。

    改进(这里的改进如果完成的话,那么和市面上一线的搜索引擎相应功能比,也就输在没有商标):

    (1)对于要满足中拼双搜的搜索框提示功能,需要维护中文、拼音两棵键树(中文拼音转化可使用pinyin4j开源库,处理时注意拼音时涉及多音字),在设计算法时会复杂很多(复杂4倍左右)。

    (2)为了提升中文键树的效率,可以考虑按照偏旁拆分中文来组织键树结点结构(就像按照偏旁部首查字典一样),将会使键树的效率提升非常多。实现这样的算法需要中文偏旁api的支持,至于有不有这样的api我就不知道了。

代码贴:中文键树的蛮力实现(可处理任意字符串)

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

import com.yhk.filewriter.MyReader;

/*
 * 无数据结构设计下的蛮力中文键树
 */
class TrieNode {
	public String value;
	public ArrayList<TrieNode> ptr = null;
	public TrieNode(String value) {
		this.value=value;
		ptr =new ArrayList<TrieNode>();
	}
}

public class TrieTree_1 {
	private static TrieNode root = null;
	ArrayList<String> searchResult=new ArrayList<String>();
	StringBuffer tempWord=new StringBuffer();
	int start=0;
	
	public TrieTree_1() {
		root = new TrieNode(null);
	}
	
	public void insert(String key) {
		TrieNode p = root;
		String tempWord;
		boolean contains;
		TrieNode tempNode;
		for (int i = 0; i < key.length(); i++) {
			tempWord=String.valueOf(key.charAt(i));
			contains=false;
			for(TrieNode tn:p.ptr){
				if(tn.value.equals(tempWord)){
					p=tn;
					contains=true;
					break;
				}
			}
			if(!contains){
				tempNode=new TrieNode(tempWord);
				p.ptr.add(tempNode);
				p=tempNode;
			}
		}
	}
	
	public ArrayList<String> search(String key) {  //模糊查询就是这个方法,打个比方比如key是"ap",那么ArrayList里就有{"apple","application"}
		TrieNode p = root;
		String temp;
		boolean contains=false;
		for (int i = 0; i < key.length(); i++) {
			temp=String.valueOf(key.charAt(i));
			contains=false;
			for(TrieNode tn:p.ptr){
				if(tn.value.equals(temp)){
					p=tn;
					contains=true;
					break;
				}
			}
			if(contains){
				continue;
			}else{
				break;
			}
		}
		if(contains){
			if(!(p.ptr.isEmpty())){
				//查找到关键字
				searchResult.clear();
				tempWord.delete(0, tempWord.length());
				tempWord.append(key);
				start=key.length();
				traverseTree(p);
			}else{
				//已经查找到键树的底部
				return null;
			}
		}else{
			//没有查找到相应关键字
			return null;
		}
		return searchResult;
	}
	
	private void traverseTree(TrieNode p){
		if(!(p.ptr.isEmpty())){
			for(TrieNode tn:p.ptr){
				tempWord.append(tn.value);
				start++;
				traverseTree(tn);
				start--;
				tempWord.delete(start,tempWord.length());
			}
		}else{
			searchResult.add(tempWord.toString());
		}
	}
}



2
1
查看评论

用java实现简单的搜索引擎

纯java实现简单的搜索引擎
  • xiaojimanman
  • xiaojimanman
  • 2014-07-19 11:10
  • 7567

Java基础-实现文件搜索功能

java实现文件搜索功能,代码示例如下:
  • morning2008
  • morning2008
  • 2016-01-18 21:42
  • 300

Java实现lucene搜索功能

直接上代码: package com.sand.mpa.sousuo; //--------------------- Change Logs---------------------- //@author zhiqiang.zhang Initial Created at 2010-12-23...
  • u010235716
  • u010235716
  • 2016-05-23 16:40
  • 6206

搜索功能设计java实现

  • 2008-01-14 23:51
  • 25KB
  • 下载

各种查找的实现Java

使用Java实现各种查找,顺序查找,快速查找,有序顺序查找,二分查找,一致对半查找,斐波那契查找,插值查找。
  • liangxiamoyi
  • liangxiamoyi
  • 2016-08-11 18:58
  • 1343

java+js实现下拉框提示搜索功能

  • 2010-08-18 14:37
  • 7KB
  • 下载

Java编程——应用开源框架实现简易web搜索

引言应用 Java 的开源库,编写一个搜索引擎,这个引擎能爬取一个网站的内容。并根据网页内容进行深度爬取,获取所有相关的网页地址和内容,用户可以通过关键词,搜索所有相关的网址。
  • qq_22187919
  • qq_22187919
  • 2017-03-05 12:00
  • 932

java 实现搜索附近人功能

java 实现搜索附近人功能
  • lchq1995
  • lchq1995
  • 2017-04-17 16:01
  • 2563

java之模糊查询

1.场景还原     由于项目中有很多地方设置了搜索框,所以搜索框之模糊查询势在必得;今晚笔者将详细讲解java之模糊查询的细节及要点,希望能给大伙带来启发。 2.实现方案 后台代码: ①UserInfo实体类的定义 public class UserInfo { pr...
  • zhangxing52077
  • zhangxing52077
  • 2017-06-26 22:23
  • 4964

经典算法1---相似度--模糊查询,查抄袭,语言识别

from http://wdhdmx.iteye.com/blog/1343856#bc2319361 1.百度百科介绍: Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。 许可的编辑操作包括将一个字符替换成另一个字符,插入...
  • basycia
  • basycia
  • 2016-07-11 21:57
  • 9940
    个人资料
    • 访问:61377次
    • 积分:955
    • 等级:
    • 排名:千里之外
    • 原创:26篇
    • 转载:0篇
    • 译文:8篇
    • 评论:17条
    博客专栏
    文章分类