Trie树_1-TableBaseTrieTree

1、引言

  TableBaseTrieTree是基于二维数组表的数据结构来存储树的状态节点和转移条件。

2、实现

状态节点:

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

  使用广度优先搜索从根节点开始遍历状态节点,得到有效数值数组

3、测试

测试代码:

 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]
江南大学:江南大学
江南:江南
江边:江边
南方:南方
云南:云南
江南:[江南, 江南大学]

4、总结

  TableBaseTrieTree实习简单,查询速度快,但是内存开销非常大。

 参考源码:https://github.com/pythonerleilei/Search/blob/master/src/seach/data_structure/tree/impl/TableBaseTrieTree.java

猜你喜欢

转载自www.cnblogs.com/searchtosuggest/p/9292118.html