三向单词查找树
为了避免R向单词查找树过度的空间消耗,可以使用三向单词查找树(TST)。在三向单词查找树中,每个结点都含有一个字符、三条链接和一个值。这三条链接分别对应着当前字母小于、等于和大于结点字母的所有键。在R向单词查找树中,树的结点含有R条链接,每个非空链接的索引隐式地表示了它所对应的字符。在等价的三向单词查找树中,字符是显式地保存在结点中的——只有在沿着中间链接前进时才会根据字符找到表中的键。
查找与插入操作
在查找时,首先比较键的首字母和根结点的字母。如果键的首字母较小,就选择左链接;如果较大,就选择右链接;如果相等,则选择中链接。然后,递归地使用相同的算法。如果遇到了一个空链接或者当键结束时结点的值为空,那么查找未命中;如果键结束时结点的值非空则查找命中。在插入新键时,首先进行查找,然后和在单词查找树一样,在树中补全键末尾的所有结点。
性质
空间
三向单词查找树最重要的性质就是每个结点只含有三个链接,因此三向单词查找树所需要空间远小于对应的单词查找树。
由N个平均长度为w的字符串构造的三向单词查找树中的链接总数在3N到3Nw之间。
查找成本
在一棵由N个随机字符串构造的三向单词查找树中,查找未命中平均需要比较字符~lnN次。除~lnN次外,一次插入或命中的查找会比较一次被查找的键中的每个字符。
代码
package section5_2;
public class TST<Value> {
private Node root;
private class Node {
char c;
Node left,mid,right;
Value val;
}
public Value get(String key) {
Node x = get(root,key,0);
if (x == null) {
return null;
}
return (Value) x.val;
}
private Node get(Node x, String key, int d) {
if (x == null) return null;
char c = key.charAt(d);
if (c < x.c) return get(x.left,key,d);
else if (c > x.c) return get(x.right,key,d);
else if (d < key.length() - 1) return get(x.mid,key,d+1);
else return x;
}
public void put(String key, Value val) {
root = put(root,key,val,0);
}
private Node put(Node x, String key, Value val, int d) {
char c = key.charAt(d);
if (x == null) {
x = new Node();
x.c = c;
}
if (c < x.c) x.left = put(x.left,key,val,d);
else if (c > x.c) x.right = put(x.right,key,val,d);
else if (d < key.length() - 1) x.mid = put(x.mid,key,val,d+1);
else x.val = val;
return x;
}
public static void main(String[] args) {
String[] a = {
"she",
"sells",
"sea",
"shells",
"by",
"the",
"shore"
};
TST<Integer> tst = new TST<>();
for (int i = 0;i < a.length;i++) {
tst.put(a[i],i);
}
System.out.println("test get:");
System.out.println(tst.get("shells"));
System.out.println(tst.get("shell"));
}
}
输出: