overview:(1)单词搜索树是一个符号表.其中键的类型为字符串.(2)树是根据字符串中的每个字符为节点来建立的,这就是区别于比如二茬搜索树的地方.(3)二叉搜索树以每个键(字符串)值对为节点建立.
R向单词查找树
1:由字符串键中的所有字符构造而成。
2:是个符号表
3:树的形状和键的插入或者删除顺序无关.对于任意给定的一组键其单词查找树是唯一的.二叉搜索树就和插入顺序有关了.
4:一颗单词查找树的链接总数在RN到RNw之间.(R:字母表大小;N:键数目;w:键的平均长度); 因此缩小R就可以节省大量的空间
package String.Tree;
import javax.naming.directory.SearchControls;
//单词查找树,其实就是一个符号表
public class TrieST<Value> {
private static int R = 256;//ascii表
private Node root;
private static class Node{
private Object val;
private Node[] next = new Node[R];
}
//根据键找值
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;
if(d == key.length()) return x;
char ch = key.charAt(d);
return get(x.next[ch], key, d+1);
}
//添加键值对
public void put(String key,Value val){
root = put(root,key,val,0);
}
private Node put(Node x, String key, Value val,int d) {
if(x == null) x = new Node();
if(d == key.length()){x.val = val; return x;}
char ch = key.charAt(d);
x.next[ch] = put(x.next[ch], key, val, d+1);
return x;
}
//删除键值对
public void delete(String key){
delete(root,key,0);
}
private Node delete(Node x, String key, int d) {
if(x == null) return null;
if(d == key.length()){
x.val = null;
}else{
char ch = key.charAt(d);
x.next[ch] = delete(x.next[ch], key, d+1);
}
//删除没用的节点
if(x.val != null) return x;
for(Node e : x.next)
if(e != null) return x;
return null;
}
//收集所有键
public Iterable<String> keys(){
return keysWithPrefix("");
}
//收集以以pre为前缀的键
private Iterable<String> keysWithPrefix(String pre) {
Queue<String> q = new Queue<String>();
collect(get(root,pre,0),pre,q);
return q;
}
//收集以以pre为前缀的键
private void collect(Node x, String pre, Queue<String> q) {
if(x == null) return;
if(x.val != null) q.enqueue(pre);
for(char c=0;c<R;c++)
collect(x.next[c],pre+c,q);
}
//通配符匹配,收集所有相匹配的字符串
public Iterable<String> keysThatMatch(String pat){
Queue<String> q = new Queue<String>();
collect(root,"",pat, q);
return q;
}
private void collect(Node x, String pre, String pat, Queue<String> q) {
int d = pre.length();
if(x == null) return;
if(d == pat.length() && x.val != null) q.enqueue(pre);
if(d == pat.length()) return;
char next = pat.charAt(d);
for(char c = 0; c<R; c++)
if(next == '.' || next == c)
collect(x.next[c], pre+c, q);
}
//查找该字符串在树中的最长前缀
public String longstPrefixOf(String s){
int length = search(root,s,0,0);
return s.substring(0,length);
}
private int search(Node x, String s, int d, int length) {
if(x == null) return length;
if(x.val != null) length = d;
if(d == s.length()) return length;
char ch = s.charAt(d);
return search(x.next[ch], s, d+1, length);
}
public static void main(String[] args) {
TrieST<Integer> tst = new TrieST<Integer>();
tst.put("shells",15);
tst.put("shore",7);
tst.put("by", 4);
tst.put("are", 12);
tst.put("sea",14);
tst.put("the", 8);
tst.put("she",10);
System.out.println(tst.longstPrefixOf("shzzzxxx"));
}
}
三向单词查找树
1:(0)三向单词查找树对应三向字符串快速排序;
(1)二叉查找树对应快速排序.
(2)R向单词查找树对应高位优先排序.单词查找树的链接所占用的空间即高位优先的字符串排序中的计数器所占用的空间.
(3)所谓三向就是将节点的儿子分为等于大于小于三个分枝.
2:树的结构取决于键的插入顺序.
3:所需的空间小.
4:只分为三向,相对于R向而言,树的高度肯定增加了.所以查找命中就需要线性对数级别了.而R向查找命中是线性级别.
package String.Tree;
//三向单词查找树,其实就是一个符号表.键为字符串类型
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 Tst<Value>.Node get(Tst<Value>.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 Tst<Value>.Node put(Tst<Value>.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 void delete(String key){
root = delete(root,key,0);
}
private Tst<Value>.Node delete(Tst<Value>.Node x, String key, int d) {
if(x == null) return null; //查找未命中,不存在该键
char ch = key.charAt(d);
if(ch > x.c) x.right = delete(x.right, key, d);
else if(ch < x.c) x.left = delete(x.left, key, d);
else if(d < key.length()-1) x.mid = delete(x.mid, key, d+1);
else {
x.val = null; //找到该键
}
if(x.val != null) return x;
//没用的节点都删除掉
if(x.left == null && x.right == null && x.mid == null)
return null;
return x;
}
//查找该字符串在树中的最长前缀
public String longstPrefixOf(String s){
int length = search(root,s,0,0);
return s.substring(0,length);
}
private int search(Tst<Value>.Node x, String s, int d, int length) {
if(x == null) return length;
if(x.val != null) length = d+1;
if(d == s.length()) return length;
char ch = s.charAt(d);
if(ch < x.c) return search(x.left, s, d, length);
else if(ch > x.c) return search(x.right, s, d, length);
else return search(x.mid, s, d+1, length);
}
//收集所有键
public Iterable<String> keys(){
Queue<String> q = new Queue<String>();
collect(root,new StringBuffer(),q);
return q;
}
//收集以以pre为前缀的键
public Iterable<String> keysWithPrefix(String pre) {
if (pre == null) {
throw new IllegalArgumentException("calls keysWithPrefix() with null argument");
}
Queue<String> q = new Queue<String>();
Tst<Value>.Node x = get(root, pre, 0);
if (x == null) {
return null;
}
if(x.val != null) q.enqueue(pre);
collect(get(root,pre,0).mid,new StringBuffer(pre),q);
return q;
}
//收集以以pre为前缀的键
private void collect(Node x, StringBuffer prefix, Queue<String> queue) {
if (x == null) return;
if(x.val != null) queue.enqueue(prefix.toString()+x.c);
collect(x.mid, prefix.append(x.c), queue);
prefix.deleteCharAt(prefix.length() - 1);
collect(x.left, prefix, queue);
collect(x.right, prefix, queue);
}
//通配符匹配,收集所有相匹配的字符串
public Iterable<String> keysThatMatch(String pat){
Queue<String> q = new Queue<String>();
collect(root,new StringBuffer(),0,pat, q);
return q;
}
private void collect(Node x, StringBuffer prefix, int i, String pattern, Queue<String> queue) {
if (x == null) return;
char c = pattern.charAt(i);
if (c == '.' || c < x.c) collect(x.left, prefix, i, pattern, queue);
if (c == '.' || c == x.c) {
if (i == pattern.length() - 1 && x.val != null) queue.enqueue(prefix.toString() + x.c);
if (i < pattern.length() - 1) {
collect(x.mid, prefix.append(x.c), i+1, pattern, queue);
prefix.deleteCharAt(prefix.length() - 1);
}
}
if (c == '.' || c > x.c) collect(x.right, prefix, i, pattern, queue);
}
public static void main(String[] args) {
Tst<Integer> tst = new Tst<Integer>();
tst.put("shells",15);
tst.put("shore",7);
tst.put("by", 4);
tst.put("are", 12);
tst.put("sea",14);
tst.put("the", 8);
tst.put("she",10);
// tst.delete("are");
// tst.delete("shells");
// tst.delete("she");
Queue<String> q = (Queue<String>) tst.keys();
for(String s : q)
System.out.println(s);
//System.out.println(tst.longstPrefixOf("arexxx"));
}
}