线段树、Trie和并查集02:Trie


Trie,又称字典树、前缀树,是一种N叉树

Trie是一种专门为字典设计的数据结构,通常只用来处理字符串;而之前的Map更适合称为映射而不是字典,因为其存储的对象不一定是字符串

如果用TreeMap和Trie来查询字符串,TreeMap的时间复杂度为O(logn),而Trie的时间复杂度和总数n无关,只和字符串的长度有关,每个节点只存储一个字符

在n个条目中查询字符串 时间复杂度
TreeMap O(logn)
Trie O(w)

实现字典树

import java.util.TreeMap;

/**
 * 字典树一般存储的都是字符串,因此不使用泛型
 */
class Trie{

    private class Node{

        /**
         * 每个节点需要一个标志isWord来判断是否是一个字符串的结尾,因为很多字符串有重叠部分
         * 每个节点,其子节点的数目是不确定的,因此定义为TreeMap类型,每个节点的子节点都是一个集合,也即N叉树
         */
        public boolean isWord;
        public TreeMap next;

        public Node(boolean isWord){

            this.isWord = isWord;
            next = new TreeMap<>();
        }

        public Node(){

            this(false);
        }
    }

    private Node root;
    private int size;

    public Trie(){

        root = new Node();
        size = 0;
    }

    public int getSize(){

        return size;
    }

    /**
     * 增
     * 从根节点出发,先判断子节点集合中有没有当前要放入的字符,没有就添加,然后继续向下寻找(寻找下一个符合的子节点使用Map类的get()方法)
     * 因为字符串可能重复,因此将新字符串的isWord设置为true,此时才能size加1
     */
    public void add(String string){

        Node cur = root;

        for (int i = 0; i < string.length(); i++) {

            char c = string.charAt(i);

            if (cur.next.get(c) == null){
                cur.next.put(c, new Node());
            }

            cur = cur.next.get(c);
        }

        if (!cur.isWord){

            cur.isWord = true;
            size++;
        }
    }

    /**
     * 查
     */
    public boolean contains(String string){

        Node cur = root;

        for (int i = 0; i < string.length(); i++) {

            char c = string.charAt(i);

            if (cur.next.get(c) == null){
                return false;
            }
            else {
                cur = cur.next.get(c);
            }
        }

        return cur.isWord;
    }

    /**
     * 前缀搜索,前缀是字符串的子集
     */
    public boolean isPrefix(String prefix){

        Node cur = root;

        for (int i = 0; i < prefix.length(); i++) {

            char c = prefix.charAt(i);

            if (cur.next.get(c) == null){
                return false;
            }
            else {
                cur = cur.next.get(c);
            }
        }

        return true;
    }
}

字典树的局限性

最大的问题:空间消耗!每个节点最少有26个子节点,都使用一个TreeMap集合来实现,空间消耗很大

优化的方向:压缩字典树、三分搜索树、后缀树

压缩字典树(Compressed Trie)

将字母合并为词缀

三分搜索树(Ternary Search Trie)

每个节点有三个孩子,分别小于、等于和大于自身