Loading

单词查找树

本篇笔记基于Java语言,是《算法 第四版》的读书笔记,为了适当简化,部分内容和原书有出入。

单词查找树和符号表差不多,符号表允许存入任意类型的键值对(当然Key是要实现了Comparable接口的)。符号表可以用二叉查找树实现。

单词查找树差不多,只不过它的键被固定为String类型。它用字符串的特性来实现高效的字符查找树功能,虽然Java的字符串实现让字符串可以以极高效的速度进行比较,但是在一些其它语言里,实现单词查找树还是很有必要的。

字符表

我们先来介绍一下字符表的概念,字符表其实就是一堆字符的有序集合。它有如下属性:

  • 字符集: 字符表中包含的所有字符
  • 基数: 字符表中包含多少字符

而且因为它是有序的,所以可以根据下标获取字符表对应位置的字符,也可以把字符映射成对应位置的下标。

所以我们可以定义一些字符表:

名称 基数 字符集 备注
BINARY 2 01 二进制数字
DNA 4 ACTG DNA碱基符号
OCTAL 8 01234567 八进制数字
ASCII 128 标准ASCII字符集
EXTENDED_ASCII 256 扩展ASCII字符集
UNICODE16 65535 Unicode字符集

有了字符表,我们就可以把具体的字符串问题限制在应用使用的字符表上了。

API

看看单词查找树的API

public class StringST 备注
void put(String key,Value value) 向表中插入键值对(值为null则删除key)
Value get(String key) 获取键key所对应的值
void delete(String key) 删除键Key和对应值
boolean contains(String key) 表中是否包含键Key
boolean isEmpty() 表是否为空
String longestPrefixOf(String s) s的前缀中最长的键
Iterable keysWithPrefix(String s) 所有以s为前缀的键
Iterable keysThatMatch(String s) 所有和s匹配的键(“.”匹配任意字符)
int size() 键值对的数量
Iterable keys() 符号表中所有键

如何构造单词查找树

假设我们用DNA碱基符号作为字符表来画一张图说明单词查找树的工作过程。

DNA碱基符号的R为4

假设我们有一个DNA序列的键ACGA,那么它将这样存储在单词查找树中。

这图画得有些不形象。单词查找树中每一个节点有R个子节点,每个子节点又会连接R个子节点。因为字符表是有序的,可以将字符映射成在字符表中的下标,所以每次向下查找需要画O(1)的时间,查找的总时间和字符串长度是线性关系O(n)。

一个约定

单词查找树使用扩展ASCII字符表,R为256,并且,如果直接将Java中的char类型数据当作int类型使用,那么它就是在字符表中的下标。

put和get

public class TrieST<Value>{
    // 基数
    private static int R = 256;
    private int length = 0;
    private Node root = new Node();

    public TrieST(){}
    public TrieST(int R){
        this.R = R;
    }

    private static class Node{
        private Object value;
        private Node[] childs = new Node[R];

    }


    public Value get(String key){
        Node n = get(root,0,key);
        if (n == null) return null;
        return (Value) n.value;
    }

    public Node get(Node node,int d,String key){
        if (node == null)return null;
        if (d == key.length()) return node;
        return get(node.childs[key.charAt(d)],d+1,key);
    }

    public void put(String key,Value value){
        put(root,0,key,value);
        length++;
    }

    public void put(Node node,int d,String key,Value value){
        Node next = node.childs[key.charAt(d)];
        if (next == null){
            next = new Node();
            node.childs[key.charAt(d)] = next;
        }
        if (d == key.length() - 1)
            next.value = value;
        else if (d < key.length() - 1)
            put(next,d+1,key,value);
    }
    public int length(){return length;}
}

测试用例:

public class TrieSTTest {
    public static void main(String[] args) {

        TrieST<Integer> trie = new TrieST();
        System.out.println(trie.get("shells"));
        trie.put("she",1);
        trie.put("sells",2);
        trie.put("sea",3);
        trie.put("shells",4);
        trie.put("by",5);
        trie.put("the",6);
        trie.put("shore",7);

        System.out.println(trie.get("shells"));
        System.out.println(trie.get("sea"));
        System.out.println(trie.get("by"));
    }
}

keys()

public Iterable<String> keys(){
    return keyWithPrefix("");
}
public Iterable<String> keyWithPrefix(String prefix){
    LinkedList<String> queue = new LinkedList<>();
    collect(get(root,0,prefix),prefix,queue);
    return queue;
}
private void collect(Node x, String pre, LinkedList<String> q){
    if (x==null) return;
    if (x.value != null) q.addLast(pre);
    for (char c = 0;c<R;c++)
        collect(x.childs[c],pre+c,q);
}

暂时实现这么多...

posted @ 2020-02-21 14:07  yudoge  阅读(522)  评论(0编辑  收藏  举报