关于Trie: 在计算机科学中,Trie,又称字典树、单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

import java.util.TreeMap;

/** * @author BestQiang */
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;
    private int size;

    public Trie() {
        root = new Node();
        size = 0;
    }

    /** * 获得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 */
    public void recursionAdd(String word) {
        Node cur = root;
        add(root, word, 0);
    }

    /** * 递归写法调用方法实现递归添加 * * @param node 传入要进行添加的节点 * @param word 传入要进行添加的单词 */
    public void add(Node node, String word, int index) {
        // 确定终止条件,这个终止条件在没加index这个参数时,很难确定
        // 此时一个单词已经遍历完成了,如果这个结束节点没有标记为单词,就标记为单词
        if (!node.isWord && index == word.length()) {
            node.isWord = true;
            size++;
        }

        if (word.length() > index) {
            char addLetter = word.charAt(index);
            // 判断trie的下个节点组中是否有查询的字符,如果没有,就添加
            if (node.next.get(addLetter) == null) {
                node.next.put(addLetter, new Node());
            }
            // 基于已经存在的字符进行下个字符的递归查询
            add(node.next.get(addLetter), word, index + 1);
        }
    }

    /** * 查询单词word是否在Trie中(非递归写法) * * @param word * @return */
    public boolean contains(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;
            } else {
                cur = cur.next.get(c);
            }
        }
        return cur.isWord;
    }

    /** * 查询单词word中是否在Trie中接口(递归写法) * * @param word * @return */
    public boolean recursionContains(String word) {
        Node cur = root;
        return contains(root, word, 0);
    }

    /** * 查询word中是否在Trie中递归写法 * * @param node * @param word * @param index * @return */
    private boolean contains(Node node, String word, int index) {
        if (index == word.length()) {
            return node.isWord;
        }
        char c = word.charAt(index);
        if (node.next.get(c) == null) {
            return false;
        } else {
            return contains(node.next.get(c), word, index + 1);
        }
    }

    /** * 查询是否在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;
    }

    /** * 查询是否在Trie中有单词一prefix为前缀(递归调用) * * @param prefix * @return */
    public boolean recursionIsPrefix(String prefix) {
        Node node = root;
        return recursionIsPrefix(root, prefix, 0);
    }

    /** * 查询是否在Trie中有单词一prefix为前缀(递归实现) * * @return */
    public boolean recursionIsPrefix(Node root, String prefix, int index) {
        if (prefix.length() == index) {
            return true;
        }
        char c = prefix.charAt(index);
        if (root.next.get(c) == null) {
            return false;
        } else {
            return recursionIsPrefix(root.next.get(c), prefix, ++index);
        }
    }
}