public class PatriciaTrieMock<T> {
//根节点
private PatriciaTrieNode root;
//设置一个节点类
private class PatriciaTrieNode<T> {
//该key是否是一个结束的标志位
private boolean isWord;
//key值
private String key;
//value值
private T value;
//子节点的数据
private List<PatriciaTrieNode<T>> children = Lists.newArrayList();
@Override
public String toString() {
return this.key + (this.isWord ? "#结束" : "") + "(子节点个数" + children.size() + ")";
}
}
//插入数据(针对字典对象插入数据)
public void insert(String key, T value) {
//key值是否为空
if (StringUtil.isEmpty(key)) {
return;
}
//如果第一次进来,根节点为空
if (root == null) {
root = new PatriciaTrieNode();
}
insert(root, key, value);
}
/**
* 删除
*
* @param key
*/
private void delete(String key) {
if (StringUtil.isEmpty(key) || root == null) {
return;
}
delete(root, key);
}
private void delete(PatriciaTrieNode currentNode, String key) {
for (int i = 0; i < currentNode.children.size(); i++) {
PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) currentNode.children.get(i);
//取出最小长度,防止比较时出现越界问题
int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
//匹配长度
int matchLength = 0;
for (; matchLength < len; matchLength++) {
//不相等直接跳出
if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
break;
}
}
//如果完全不匹配
/**
*
* root
* / \
* 最近# 杭州#
* / \ 删除“杭州”
* 三# 这些年#
*
*/
if (matchLength == 0) {
//先比较删除的key首字母是不是应该排在当前节点key前面,如果是则说明没有找到
if (key.charAt(matchLength) > chirdrenCurrentNode.key.charAt(matchLength)) {
//不满足则下一轮
continue;
} else {
break;
}
//可以完全匹配上
/**
* 1.要删除key的长度和当前节点key长度一致
* 2.要删除key的长度 > 当前节点key长度,需要递归删除
*/
} else if (matchLength == len) {
if (key.length() == chirdrenCurrentNode.key.length()) {
/**
* root
* /
* 最近#
* / \ 删除“最近”
* 三# 这些年#
*/
currentNode.children.remove(i);
} else if (key.length() > chirdrenCurrentNode.key.length()) {
String childrenKey = key.substring(matchLength);
//递归调用delete方法插入
delete(chirdrenCurrentNode, childrenKey);
//如果子节点只有一个,且当前节点没有存放数据,需要merge
if (chirdrenCurrentNode.children.size() == 1 && !chirdrenCurrentNode.isWord) {
PatriciaTrieNode childrenNode = (PatriciaTrieNode) chirdrenCurrentNode.children.get(0);
childrenNode.key = chirdrenCurrentNode.key + childrenNode.key;
currentNode.children.set(i, childrenNode);
}
}
}
}
}
//针对节点本身添加数据
private void insert(PatriciaTrieNode currentNode, String key, T value) {
Boolean isNeedInsert = true;
//循环节点所有子节点插入数据
for (int i = 0; i < currentNode.children.size(); i++) {
PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) currentNode.children.get(i);
//取出最小长度,防止比较时出现越界问题
int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
//匹配长度
int matchLength = 0;
for (; matchLength < len; matchLength++) {
//不相等直接跳出
if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
break;
}
}
//如果完全不匹配
/**
*
* root root
* / / \
* 最近# 最近# 杭州#
* / \ 插入“杭州” / \
* 三# 这些年# 三# 这些年#
*
*/
if (matchLength == 0) {
//先比较插入的key首字母是不是应该排在当前节点key前面
// 杭<最
if (key.charAt(matchLength) < chirdrenCurrentNode.key.charAt(matchLength)) {
PatriciaTrieNode patriciaTrieNodeNew = new PatriciaTrieNode();
currentNode.children.add(patriciaTrieNodeNew);
patriciaTrieNodeNew.key = key;
patriciaTrieNodeNew.value = value;
patriciaTrieNodeNew.isWord = true;
isNeedInsert = false;
break;
} else {
//不满足则下一轮
continue;
}
//可以匹配上
} else {
//如果完全匹配,会有三种情况
/**
* 插入的key长度小于当前节点key长度
* 插入的key长度大于当前节点key长度
* 插入的key长度等于当前节点key长度
*/
if (matchLength == len) {
/**
*1. 插入的key长度小于当前节点key长度
* root root
* / /
* 最近# 最#
* / \ 插入“最” /
* 三# 这些年# 近#
* / \
* 三# 这些年#
*
*/
if (key.length() < chirdrenCurrentNode.key.length()) {
//设置子节点数据
String childrenKey = chirdrenCurrentNode.key.substring(matchLength);
PatriciaTrieNode patriciaTrieNode = new PatriciaTrieNode();
patriciaTrieNode.key = childrenKey;
patriciaTrieNode.isWord = chirdrenCurrentNode.isWord;
patriciaTrieNode.value = chirdrenCurrentNode.value;
//设置当前节点的数据
chirdrenCurrentNode.key = key;
chirdrenCurrentNode.value = value;
chirdrenCurrentNode.isWord = true;
chirdrenCurrentNode.children = Lists.newArrayList();
chirdrenCurrentNode.children.add(patriciaTrieNode);
/**
*2. 插入的key长度大于当前节点key长度
* root root
* / /
* 最近# 最近#
* / \ 插入“最近好难” / \ \
* 三# 这些年# 三# 这些年# 好难#
*/
} else if (key.length() > chirdrenCurrentNode.key.length()) {
String childrenKey = key.substring(matchLength);
//递归调用insert方法插入
insert(chirdrenCurrentNode, childrenKey, value);
/**
*3. 插入的key长度等于当前节点key长度
* root root
* / /
* 最近# 最近#
* / \ 插入“最近” / \
* 三# 这些年# 三# 这些年#
*
*/
} else {
//新值覆盖旧值
chirdrenCurrentNode.value = value;
chirdrenCurrentNode.isWord = true;
}
//只能匹配一部分
/**
*3. 插入的key长度等于当前节点key长度
* root root
* / /
* 最近# 最#
* / \ 插入“最少金额” / \
* 三# 这些年# 近# 少金额#
* / \
* 三# 这些年#
*/
} else {
//设置插入key的子节点
String newKey = key.substring(matchLength);
PatriciaTrieNode patriciaTrieNodeNew = new PatriciaTrieNode();
patriciaTrieNodeNew.key = newKey;
patriciaTrieNodeNew.isWord = true;
patriciaTrieNodeNew.value = value;
//设置当前节点的子节点
String childrenKey = chirdrenCurrentNode.key.substring(matchLength);
PatriciaTrieNode patriciaTrieNodeChildren = new PatriciaTrieNode();
patriciaTrieNodeChildren.key = childrenKey;
patriciaTrieNodeChildren.isWord = chirdrenCurrentNode.isWord;
patriciaTrieNodeChildren.value = chirdrenCurrentNode.value;
patriciaTrieNodeChildren.children = chirdrenCurrentNode.children;
//设置当前节点的最新值
chirdrenCurrentNode.key = chirdrenCurrentNode.key.substring(0, matchLength);
chirdrenCurrentNode.isWord = false;
chirdrenCurrentNode.value = null;
chirdrenCurrentNode.children = Lists.newArrayList();
//比较添加
if (newKey.charAt(0) < childrenKey.charAt(0)) {
chirdrenCurrentNode.children.add(patriciaTrieNodeNew);
chirdrenCurrentNode.children.add(patriciaTrieNodeChildren);
} else {
chirdrenCurrentNode.children.add(patriciaTrieNodeChildren);
chirdrenCurrentNode.children.add(patriciaTrieNodeNew);
}
}
//只要能匹配上一个字符,后续的循环都跳出
isNeedInsert = false;
break;
}
}
//如果没有子节点,则直接把key放在在一个新生成的子节点上
/**
* 1.key的首字符需要放在所有子节点后面
* 2.当前节点没有子节点
* root root
* / /
* 最近# 最近#
* / \ 插入“最近三年” / \
* 三# 这些年# 三# 这些年#
* /
* 年#
*/
if (isNeedInsert) {
PatriciaTrieNode patriciaTrieNode = new PatriciaTrieNode();
patriciaTrieNode.key = key;
patriciaTrieNode.value = value;
patriciaTrieNode.isWord = true;
currentNode.children.add(patriciaTrieNode);
}
}
/**
* 前缀匹配
*
* @param key
*/
private List<T> prefixMatch(String key) {
if (StringUtil.isEmpty(key) || root == null) {
return null;
}
return prefixMatch(root, key);
}
/**
* 前缀匹配
*
* @param root
* @param key
* @return
*/
private List<T> prefixMatch(PatriciaTrieNode root, String key) {
List<T> result = Lists.newArrayList();
for (int i = 0; i < root.children.size(); i++) {
PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) root.children.get(i);
//取出最小长度,防止比较时出现越界问题
int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
//匹配长度
int matchLength = 0;
for (; matchLength < len; matchLength++) {
//不相等直接跳出
if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
break;
}
}
//一个都没匹配上
/**
* 1. 查询key的首字母比当前节点key首字母大,那么有可能在后续节点匹配
* 2. 查询key的首字母比当前节点key首字母小,那么确实匹配不上
*/
/**
* 查询的key为“好难呀” or “真的”
* root
* / \
* 最近# 好难呀#
* / \
* 三# 这些年#
*
*
*/
if (matchLength == 0) {
if (key.charAt(matchLength) < chirdrenCurrentNode.key.charAt(matchLength)) {
break;
} else {
continue;
}
//如果完全匹配(匹配长度==最小长度)
} else if (matchLength == len) {
/**
* 查询的key为“最近这”
* root
* / \
* 最近# 好难呀#
* / \
* 三# 这些年#
*/
if (key.length() > chirdrenCurrentNode.key.length()) {
String findKey = key.substring(matchLength);
result = prefixMatch(chirdrenCurrentNode, findKey);
} else {
/**
* 查询的key为“最近” 或者"最"
* root
* / \
* 最近# 好难呀#
* / \
* 三# 这些年#
*/
if (chirdrenCurrentNode.isWord) {
result.add((T) chirdrenCurrentNode.value);
}
//获取子节点的值
getChildrenValue(chirdrenCurrentNode, result);
}
}
//如果不能完全匹配,则相当于匹配不上任何值
/**
* 查询的key为“好难搞”
* root
* / \
* 最近# 好难呀#
* / \
* 三# 这些年#
*/
}
return result;
}
/**
* 递归获取子节点的值
*
* @param chirdrenCurrentNode
* @param result
*/
private void getChildrenValue(PatriciaTrieNode<T> chirdrenCurrentNode,
List<T> result) {
for (int i = 0; i < chirdrenCurrentNode.children.size(); i++) {
PatriciaTrieNode patriciaTrieNode = chirdrenCurrentNode.children.get(i);
if (CollectionUtils.isNotEmpty(patriciaTrieNode.children)) {
getChildrenValue(patriciaTrieNode, result);
}
if (patriciaTrieNode.isWord) {
result.add((T) patriciaTrieNode.value);
}
}
}
public static void main(String[] args) {
PatriciaTrieMock<String> patriciaTrieMock = new PatriciaTrieMock<String>();
patriciaTrieMock.insert("最近三年", "最近三年");
patriciaTrieMock.insert("最近有点难", "最近有点难");
patriciaTrieMock.insert("你好", "你好");
patriciaTrieMock.insert("这是patriciaTrieste的实现", "这是patriciaTrieste的实现");
patriciaTrieMock.insert("这是xxx的实现", "这是xxx的实现");
System.out.println(patriciaTrieMock);
List<String> result = patriciaTrieMock.prefixMatch("最近");
result.stream().forEach(obj -> System.out.println(obj));
patriciaTrieMock.delete("这是xxx的实现");
result = patriciaTrieMock.prefixMatch("这是");
result.stream().forEach(obj -> System.out.println(obj));
}
}