D-Labels : 为节点中的每一个值做一个分支标记。例如根节点有以 f,s 和 t作为前缀的三个分支,那么会将这个大小为256的bit map的第 102(f),115(s) 和 116 (t)bit 位就会设置为 1。可以看到,具体哪一个bit 位,就是 ASCII 码的值。
D-HasChild : 标记对应的子节点是否是叶子节点还是中间节点。以根节点的三个分支为例,f 和 t 都有子节点,而 s 没有,所以 102 和 116 bit 都会设置为 1。
D-IsPrefixKey : 标记当前前缀是否是有效的key。
D-Values : 存储的是固定大小的 value,在本文中,表示的是指向之前说过三种后缀(hashed, Real, Mixed)的指针。
现在仍然可以使用select&rank操作来访问Trie树中LOUDS-Dense对应的节点:
求孩子节点:假设某一结点的label分支有节点,即对应的D-HasChild[pos] = 1,则对应的分支的孩子节点的位置是 D-ChildNodePos(pos)=256×rank1(D-HasChild,pos)
举例:求根节点的中D-Label为t的孩子节点(D-HasChild(pos)=1)分支,Position(t) = 116,则:
D-ChildNodePos(256)=256×rank1(D-HasChild,pos) = 256 * 2= 512 //第三个节点的起始位置为512。
注:operator(seq,pos) 表示在序列seq上做operator操作,上式就是在D-HasChild中做rank1(pos)操作
求父亲节点:假设求pos=623(第三个节)的父亲位置:
D-ParentNodePos(pos) = select1(D-HasChild, ⌊pos/256⌋)
带入公式得D-ParentNodePos(pos) = select1(D-HasChild,⌊623/256⌋) = select1(D-HasChild, 2) = 116
2. LOUDS-Sparse
使用3个bit序列来对trie树进行编码, 在整个bit序列中, 每个节点的长度相同, 这三个bit序列分别是:
S-Labels(bit-sequences) : 直接存储节点中的值,按照 level order 的方式记录了所有 node 的 label,用0xFF($)标记该前缀也是key节点(作用相当于LOUDS-Dense中的D-IsPrefixKey )。
S-HasChild(one bit) : 记录每个节点中的label是否含有分支子节点, 有的话标记为1, 每个label使用一个bit。
S-LOUDS(one bit) : 记录每个label是否是该节点的第一个label。譬如上图第三层,r,p 和 i 都是本节点的第一个label,那么对应的 S-LOUDS 就设置为 1 了。
S-Values : 存储的是固定大小的 value,在本文中,表示的是指向之前说过三种后缀(hashed, Real, Mixed)的指针。
使用select&rank操作来访问Trie树中LOUDS-Sparse对应的节点:
求孩子节点:假设某一结点的label分支有节点,即对应的S-HasChild[pos] = 1,则对应label分支的孩子节点的位置是:S-ChildNodePos(pos) = select1(S-LOUDS,rank1(S-HasChild,pos) + 1)
例如,S-HasChild[5]=1,rank1(S-HasChild, pos) = 2 + 5 =7(这里要加上LOUDS-Dense上的D-HasChild),select1(S-LOUDS, 7 + 1) = 9(S-LOUDS主要代表节点的label边界,需要减去LOUDS-Dense上的3个节点,实际上求的是select1(S-LOUDS, 8-3))
求父亲节点:假设求pos=623(第三个节)的父亲位置:
S-ParentNodePos(pos) = select1(S-HasChild, rank1(S-LOUDS, pos) -1);
例如,现在求pos = 9的父节点,rank1(S-LOUDS, pos) = 8( rank1(S-LOUDS, pos) = 5 但是加上LOUDS-Dense上的3个节点)select1(S-HasChild, 7) = 6 (S-HasChild还包括了LOUDS-Dense上的D-HasChild)
性能分析假设这棵Trie树有H层,LOUDS-Dense-Size(l) 表示从0到l(不包含l)层采用LOUDS-Dense编码,而LOUDS-Sparse-Size(l) 表示从l到H层采用LOUDS-Sparse方式编码,这棵树按多少比例采用两种方式去编码:
LOUDS-Dense-Size(l) × R ≤ LOUDS-Sparse-Size(l) //通常R默认值是64
于是,LOUDS-Sparse方式的编码大小会决定这棵Trie树的实际编码空间大小。现在给定n个个关键字的集合,S-labes需要使用8n个bits, S-HasChild和S-LOUDS一共使用2n个bits, 所以LOUDS-Sparse使用10n个bits。而Dense占用的空间要远远小于Sparse部分,所以整个LOUDS-DS编码的Trie树接近10n个bits。
性能测试