年关将至,公司没啥事了,闲来无聊,自己写点感兴趣的东西,今天使用Java来实现一下前缀树.
首先定义前缀树节点对象,一个内部类:
static class TireNode<E> {
private E v;
private TireNode<E> [] children;
public TireNode(){
}
public TireNode(E v){
this.v = v;
}
public TireNode(E v, TireNode<E>[] children){
this.v = v;
this.children = children;
}
public E getV() {
return v;
}
public void setV(E v) {
this.v = v;
}
public TireNode<E>[] getChildren() {
return children;
}
public void setChildren(TireNode<E>[] children) {
this.children = children;
}
}
接下来在类中定义一个root节点,并且编写添加函数:
public class TestTireTree {
private static TireNode root;
public static TireNode getRoot() {
return root;
}
public void setRoot(TireNode root) {
this.root = root;
}
public static void add(String s){
Assert.notNull(s, "element must not null ");
Character[] chars = toCharArray(s);
addElement(chars);
}
}
接下来是添加元素函数:
/**
* 添加前缀树元素
* @param arr
* @param <E>
*/
private static <E> void addElement(E[] arr){
if(root == null){
addNew(arr);
} else{
Pair<Integer, TireNode<E>> matching = matching(arr, root);
TireNode<E> node = matching.getRight();
TireNode<E>[] children = node.getChildren();
TireNode<E>[] newChildren = new TireNode[children.length+1];
addAll(children, newChildren);
TireNode<E> firstNode = new TireNode<>(arr[matching.getLeft()]);
newChildren[newChildren.length-1] = firstNode;
append(Arrays.copyOfRange(arr, matching.getLeft()+1, arr.length), firstNode);
node.setChildren(newChildren);
}
}
接下来就是一些相关的函数了:
/**
* 数组扩容
* @param origin
* @param target
* @param <E>
*/
private static <E> void addAll(E[] origin, E[] target) {
Assert.isTrue(target.length >= origin.length, "target's length must be longer than origin's");
for(int i=0;i< origin.length;++i){
target[i] = origin[i];
}
}
/**
* 字符串转数组
* @param s
* @return
*/
private static Character[] toCharArray(String s){
char[] chars = s.toCharArray();
Character[] result = new Character[chars.length];
for(int i=0;i< chars.length;++i){
result[i] = chars[i];
}
return result;
}
/**
* 查找前缀树匹配的最后的一个节点
* @param arr
* @param node
* @param <E>
* @return 第一个返回值是当前字符串能匹配到的最后一个字符的下标
* 第二个返回值是最后一个匹配到的字符对应的节点
*/
private static <E> Pair<Integer, TireNode<E>> matching(E[] arr, TireNode<E> node){
int idx = 0;
if(node != null){
for(int i=0;i<arr.length;++i){
E v = arr[i];
TireNode<E>[] children = node.getChildren();
TireNode<E> exist = exist(v, children);
if(exist == null){
return new ImmutablePair<>(idx, node);
}
++idx;
node = exist;
}
}
return new ImmutablePair<>(idx, node);
}
/**
* 判断当前值是否存在于前缀树中
* @param v
* @param children
* @param <E>
* @return
*/
private static <E> TireNode<E> exist(E v, TireNode<E>[] children){
if(children != null && children.length > 0){
for(int i=0;i<children.length;++i){
if(children[i].getV() == v){
return children[i];
}
}
}
return null;
}
/**
* 添加第一个节点
* @param arr
* @param <E>
*/
private static <E> void addNew(E[] arr){
root = new TireNode();
append(arr, root);
}
/**
* 追加前缀树新节点
* @param arr
* @param parent
* @param <E>
*/
private static <E> void append(E[] arr, TireNode parent){
TireNode<E>[] children = new TireNode[1];
TireNode<E> tmp = null;
for(int i=0;i< arr.length;++i){
TireNode<E> node = new TireNode<>(arr[i]);
if(null == tmp) {
children[0] = node;
}
else{
TireNode<E>[] tmpChildren = new TireNode[1];
tmpChildren[0] = node;
tmp.setChildren(tmpChildren);
}
tmp = node;
}
parent.setChildren(children);
}
搞定!