标签:检索 void .com each 简单 使用 roo 信息 arc
TableBaseTrieTree是基于二维数组表的数据结构来存储树的状态节点和转移条件。
状态节点:
1 private class Node{ 2 3 private T t; 4 private List<Integer> next = new ArrayList<>(); 5 6 public Node() { 7 for(int i = 0; i < 256; i++) { 8 this.next.add(null); 9 } 10 } 11 12 public T getT() { 13 return t; 14 } 15 16 public void setT(T t) { 17 this.t = t; 18 } 19 20 public List<Integer> getNext() { 21 return next; 22 } 23 }
其中t为状态节点中的有效信息,next中保存着该状态节点向下一个状态节点的转移条件
声明字段:
1 private List<Node> states = new ArrayList<>();
states中顺序保存着所有的状态节点
构造函数:
1 public TableBaseTrieTree() { 2 Node root = new Node(); 3 this.states.add(root); 4 }
初始化root状态节点并加入节点数组
这个时候的状态是这样的:
status:
0
左侧第一列为状态节点,看出树中当前只有一个root节点,状态转移数组无值
字符串插入:
1 @Override 2 public void insert(String str, T t) { 3 if(str != null && str.length() > 0) { 4 byte[] bs = str.getBytes(); 5 int parent = 0; 6 for(int i = 0; i < bs.length; i++) { 7 parent = this.insert(parent, bs[i]); 8 if(i == bs.length - 1) { 9 this.states.get(parent).setT(t); 10 } 11 } 12 } 13 } 14 15 private int insert(int parent, byte b) { 16 List<Integer> parentNext = this.states.get(parent).getNext(); 17 Integer child = parentNext.get(b + 127); 18 if(child == null) { 19 Node childNode = new Node(); 20 this.states.add(childNode); 21 child = this.states.size() - 1; 22 parentNext.set(b + 127, child); 23 } 24 return child; 25 }
为了Trie树的通用性,在插入字符串时,将字符串转为字节数组的形式,字节的值为状态转移数组next中对应的索引值,索引对应的值为下一个状态节点在states数组的索引值,next数组字节索引对应的值为null时,新建一个状态节点,插入states数组的尾部,并把转移条件设为其索引值;在最后的字节插入后对应的状态节点中插入有效值t。
插入字符串”江南大学“后的状态为:
status:
0 101:1
1 48:2
2 30:3
3 100:4
4 12:5
5 22:6
6 100:7
7 35:8
8 38:9
9 100:10
10 44:11
11 37:12
12:江南大学
插入字符串”江南“后的状态为:
status:
0 101:1
1 48:2
2 30:3
3 100:4
4 12:5
5 22:6
6:江南 100:7
7 35:8
8 38:9
9 100:10
10 44:11
11 37:12
12:江南大学
插入字符串”江边“后的状态为:
status: 0 101:1 1 48:2 2 30:3 3 100:4 103:13 4 12:5 5 22:6 6:江南 100:7 7 35:8 8 38:9 9 100:10 10 44:11 11 37:12 12:江南大学 13 61:14 14 56:15 15:江边
插入字符串”南方“后的状态为:
status: 0 100:16 101:1 1 48:2 2 30:3 3 100:4 103:13 4 12:5 5 22:6 6:江南 100:7 7 35:8 8 38:9 9 100:10 10 44:11 11 37:12 12:江南大学 13 61:14 14 56:15 15:江边 16 12:17 17 22:18 18 101:19 19 21:20 20 56:21 21:南方
插入字符串”云南“后的状态为:
status: 0 99:22 100:16 101:1 1 48:2 2 30:3 3 100:4 103:13 4 12:5 5 22:6 6:江南 100:7 7 35:8 8 38:9 9 100:10 10 44:11 11 37:12 12:江南大学 13 61:14 14 56:15 15:江边 16 12:17 17 22:18 18 101:19 19 21:20 20 56:21 21:南方 22 57:23 23 16:24 24 100:25 25 12:26 26 22:27 27:云南
字符串检索:
1 @Override 2 public T search(String str) { 3 if(str != null && str.length() > 0) { 4 byte[] bs = str.getBytes(); 5 int parent = 0; 6 for(int i = 0; i < bs.length; i++) { 7 parent = this.search(parent, bs[i]); 8 } 9 if(parent >= 0 && parent < this.states.size()) { 10 return this.states.get(parent).getT(); 11 } 12 } 13 return null; 14 } 15 16 private int search(int parent, byte b) { 17 if(parent >= 0 && parent < this.states.size()) { 18 List<Integer> parentNext = this.states.get(parent).getNext(); 19 Integer child = parentNext.get(b + 127); 20 if(child != null) { 21 return child; 22 } 23 } 24 return -1; 25 }
检索时和插入使用一样大的方法
前缀检索:
@Override public List<T> prefixSearch(String prefix) { List<T> result = new LinkedList<>(); if(prefix != null && prefix.length() > 0) { byte[] bs = prefix.getBytes(); int parent = 0; for(int i = 0; i < bs.length; i++) { parent = this.search(parent, bs[i]); } if(parent >= 0 && parent < this.states.size()) { List<Integer> q = new LinkedList<>(); q.add(parent); while(q.size() > 0) { int head = q.remove(0); T t = this.states.get(head).getT(); if(t != null) { result.add(t); } q.addAll(this.states .get(head) .getNext() .stream() .filter( e -> e != null) .collect(Collectors.toList())); } } } return result; }
使用广度优先搜索从根节点开始遍历状态节点,得到有效数值数组
测试代码:
1 public static void main(String[] args) { 2 Map<String, Integer> dict = new HashMap<>(); 3 dict.put("江南", 1); 4 dict.put("江南大学", 2); 5 dict.put("江边", 3); 6 dict.put("云南", 4); 7 dict.put("南方", 6); 8 9 TrieTree<Integer> tree1 = new TableBaseTrieTree<>(); 10 for(Entry<String, Integer> entry : dict.entrySet()) { 11 tree1.insert(entry.getKey(), entry.getValue()); 12 } 13 14 for(String key: dict.keySet()) { 15 System.out.println(key + ":" + tree1.search(key)); 16 } 17 18 System.out.println("江南:" + tree1.prefixSearch("江南")); 19 20 TrieTree<String> tree2 = new TableBaseTrieTree<>(); 21 for(String key: dict.keySet()) { 22 tree2.insert(key, key); 23 } 24 25 for(String key: dict.keySet()) { 26 System.out.println(key + ":" + tree2.search(key)); 27 } 28 29 System.out.println("江南:" + tree2.prefixSearch("江南")); 30 }
结果:
江南大学:2 江南:1 江边:3 南方:6 云南:4 江南:[1, 2] 江南大学:江南大学 江南:江南 江边:江边 南方:南方 云南:云南 江南:[江南, 江南大学]
TableBaseTrieTree实习简单,查询速度快,但是内存开销非常大。
参考源码:https://github.com/pythonerleilei/Search/blob/master/src/seach/data_structure/tree/impl/TableBaseTrieTree.java
标签:检索 void .com each 简单 使用 roo 信息 arc
原文地址:https://www.cnblogs.com/searchtosuggest/p/9292118.html