一、引言

字典树是专门为字符串处理设计的,字典树可以做到查询每个条目的时间复杂度和字段中一共有多少条目无关,跟查询的字符串长度相关。

1、什么是字典树

  • 字典树结构

字典树.png

说明:上图表示的是一颗只存英文单词的字典树,假设只包含小写英文字母,以上字典树中存储了6个单词,每个节点可以有26个指向下个节点的指针

二、实现

  • 基于TreeMap实现字典树
import java.util.TreeMap;

/**
 * 字典树、前缀树实现
 */
public class Trie {

    /**
     * 节点
     */
    private class Node {
        /**
         * 是否是单词表示
         */
        public boolean isWord;

        /**
         * 节点的子节点映射
         */
        public TreeMap<Character, Node> next;

        public Node(boolean isWord) {
            this.isWord = isWord;
            next = new TreeMap<>();
        }

        public Node() {
            this(false);
        }
    }

    /**
     * 根节点
     */
    private Node root;

    /**
     * Trie树中存储的单词数量
     */
    private int size;

    /**
     * 获取Trie中存储的单词数量
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 向Trie中添加一个新的单词Word
     * @param word 单词
     */
    public void add(String word) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.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++;
        }
    }

    /**
     * 判断Trie中是否包含单词Word
     * @param word 待查询单词
     * @return
     */
    public boolean contain(String word) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (cur.next.get(c) == null) {
                return false;
            }
            cur = cur.next.get(c);
        }
        return cur.isWord;
    }

    /**
     * 判断Trie中是否包含前缀prefix
     * @param prefix
     * @return
     */
    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;
            }
            cur = cur.next.get(c);
        }
        return true;
    }

}

三、字典树应用

1、力扣677号问题(键值映射)

  • 问题描述:实现一个 MapSum 类里的两个方法,insert 和 sum。对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
  • 问题示例
    输入: insert("apple", 3), 输出: Null
    输入: sum("ap"), 输出: 3
    输入: insert("app", 2), 输出: Null
    输入: sum("ap"), 输出: 5
  • 问题实现
import java.util.TreeMap;

/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum obj = new MapSum();
 * obj.insert(key,val);
 * int param_2 = obj.sum(prefix);
 */
class MapSum {

    /**
     * 节点
     */
    private class Node {

        /**
         * 单词结束节点保存的值
         */
        public int value;

        /**
         * 节点的子节点映射
         */
        public TreeMap<Character, Node> next;

        public Node(int value) {
            this.value = value;
            next = new TreeMap<>();
        }

        public Node() {
            this(0);
        }
    }

    /**
     * 根节点
     */
    private Node root;

    /** Initialize your data structure here. */
    public MapSum() {
        root = new Node();
    }

    /**
     * 向Trie树中插入单词及其值
     * @param word 单词
     * @param val 值
     */
    public void insert(String word, int val) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (cur.next.get(c) == null) {
                cur.next.put(c, new Node());
            }
            cur = cur.next.get(c);
        }
        cur.value = val;
    }

    /**
     * 前缀求和
     * @param prefix
     * @return
     */
    public int sum(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 0;
            }
            cur = cur.next.get(c);
        }
        return sum(cur);
    }

    private int sum(Node node) {
        int res = node.value;
        for (Character c : node.next.keySet()) {
            res += sum(node.next.get(c));
        }
        return res;
    }

}

四、时间复杂度分析

1、基于TreeMap实现的字典树实现

  • 添加单词操作:遍历待插入的单词,逐层插入各个字符与节点的映射,此操作与字典树中单词个数无关,与单词的字符长度有关,所以时间复杂度为O(w),w指单词字符长度。
  • 查询单词操作:同添加单词操作,逐层遍历单词字符,时间复杂度为O(w),w指单词字符长度。

五、其它数据结构


neojayway
52 声望10 粉丝

学无止境,每天进步一点点