LeetCode-208. 实现 Trie (前缀树)-Java-medium

法一

/**
 * 前缀树
 * (1)Trie树其实就是维护有公共前缀子串的树
 * (2)利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较
 * 复杂度
 * (1)时间复杂度:初始化为O(1),插入或查找为O(N),N为插入或查找的字符串的长度
 * (2)空间复杂度:O(26*N),N表示Trie结点数量
 */
public class Solution208 {
          
   
    /**
     * 前缀树根节点
     */
    private TrieNode root;

    /**
     * 前缀树节点静态内部类
     */
    private static class TrieNode {
          
   
        boolean isEnd; // 标记每个单词的结尾
        TrieNode[] children;

        private TrieNode() {
          
   
            isEnd = false;
            children = new TrieNode[26]; // 每个节点最多有26个不同的小写字母
        }
    }

    /**
     * 初始化前缀树
     */
    public Solution208() {
          
   
        root = new TrieNode(); // 先构造出一个空的根节点
    }

    /**
     * 向前缀树插入单词word
     *
     * @param word
     */
    public void insert(String word) {
          
   
        TrieNode cur = root; // 从根节点开始,一直向下走
        for (int i = 0; i < word.length(); i++) {
          
    // 单词每个字母正着插
            int c = word.charAt(i) - a; // 将字母用数字表示出来,并作为下标
            if (cur.children[c] == null) {
          
   
                cur.children[c] = new TrieNode();
            }
            cur = cur.children[c];
        }
        cur.isEnd = true; // 单词插入完毕,此时cur指向的节点即为该单词的结尾
    }

    /**
     * 判断一个单词word是否完整存在于前缀树中(非递归)
     *
     * @param word
     * @return
     */
    public boolean search(String word) {
          
   
        TrieNode cur = root;
        for (int i = 0; i < word.length(); i++) {
          
   
            int c = word.charAt(i) - a;
            if (cur.children[c] == null) {
          
    // 走到了null,说明word不是前缀树的任何一条路径,返回false
                return false;
            }
            cur = cur.children[c];
        }
        return cur.isEnd; // 若cur指向单词末尾,返回true,否则说明word仅仅是一个前缀,并不完整,返回false
    }

    /**
     * 根据word和start得到此时的字符,然后看该字符是否与此时的节点node配对
     *
     * @param word
     * @param node
     * @param start
     * @return
     */
    private boolean match(String word, TrieNode node, int start) {
          
   
        if (start == word.length()) {
          
   
            return node.isEnd;
        }
        int c = word.charAt(start) - a;
        return node.children[c] != null && match(word, node.children[c], start + 1);
    }

    /**
     * 判断一个单词word是否完整存在于前缀树中(递归)
     *
     * @param word
     * @return
     */
    public boolean search_2(String word) {
          
   
        return match(word, root, 0);
    }

    /**
     * 判断一个单词word是否是前缀树中的前缀
     *
     * @param prefix
     * @return
     */
    public boolean startsWith(String prefix) {
          
   
        TrieNode cur = root;
        for (int i = 0; i < prefix.length(); i++) {
          
   
            int c = prefix.charAt(i) - a;
            if (cur.children[c] == null) {
          
   
                return false;
            }
            cur = cur.children[c];
        }
        return true; // 不关心此时cur是不是指向单词末尾,如果prefix安全走完了,直接返回true即可
    }
}

本地测试

/**
         * 208. 实现 Trie (前缀树)
         */
        lay.showTitle(208);
        Solution208 sol208 = new Solution208();
        sol208.insert("apple");
        System.out.println(sol208.search("apple")); // 返回 True
        System.out.println(sol208.search("app"));   // 返回 False
        System.out.println(sol208.startsWith("app"));     // 返回 True
        sol208.insert("app");
        System.out.println(sol208.search_2("app"));   // 返回 True
经验分享 程序员 微信小程序 职场和发展