Loading

力扣 - 208. 实现Trie(前缀树)

题目

208. 实现 Trie (前缀树)

思路

  • 在我们生活中很多地方都用到了前缀树:自动补全,模糊匹配,九宫格打字预测等等。。。

  • 虽然说用哈希表也可以实现:是否出现过该单词,该单词出现过几次,但是,在某些情况下,效率并不高:比如我们要查他的前缀是否出现过,那么用哈希表就不好实现了,用前缀树更适合更简单

  • 前缀树:将单词的拆分成每一个字符,构建成树的形状,用空间换时间

  • 假设有b,abc,abd,bcd,abcd,efg,hii 这6个单词,我们构建的树就是如下图这样的:

  • 假如用要查找abcd是否存在,用最暴力的方法就是查询字串是否存在,存在继续查找下一个,时间复杂度就是\(O(N^2)\)。现在我们用前缀树是这样子查找的:第一个字符是a,那么b、e、h都不用考虑了;然后b,匹配;然后c匹配,右孩子d不用考虑;然后最后一个字符d也匹配,所以字符串存在。因此使用前缀树直接将时间复杂度压缩到了\(O(N)\),但是空间复杂度增加了,因为我们需要构建一棵前缀树来存储字符

  • 前缀树里面存储的都是26个英文小写字母,每次查找的时候,先看看当前单词在children中是否存在,存在的话就查找下一个,如果不存在直接返回false,如果字符串都遍历完了,那么就是存在的

  • 同时我们再TrieNode中添加两个属性:countpreCountcount用来统计当前单词的个数有多少个,每次插入相同的单词,那么就增加1;preCount用来统计当前字符出现的个数(用来查看某个前缀出现了多少次)

代码

class Trie {

    TrieNode root;

    public Trie() {
        // 初始化根节点
        root = new TrieNode();
    }
    
    /** 
    * 插入单词
    */
    public void insert(String word) {
        TrieNode node = root;

        for (int i = 0; i < word.length(); i++) {
            // 看看单词中的字符是否在前缀树中,空的话就添加
            if (node.children[word.charAt(i) - 'a'] == null) {
                node.children[word.charAt(i) - 'a'] = new TrieNode();
            }
            // 将node指向下一个
            node = node.children[word.charAt(i) - 'a'];
            node.preCount++;
        }
        node.count++;
    }
    
    /**
    * 查找单词是否存在
    */
    public boolean search(String word) {
        TrieNode node = root;

        for (int i = 0; i < word.length(); i++) {
            if (node.children[word.charAt(i) - 'a'] == null) {
                return false;
            }
            node = node.children[word.charAt(i) - 'a'];
        }
        // 如果count大于0,就说明单词存在,反之不存在
        return node.count > 0;
    }
    
    /**
    * 查找前缀是否存在
    */
    public boolean startsWith(String prefix) {
        TrieNode node = root;

        for (int i = 0; i < prefix.length(); i++) {
            if (node.children[prefix.charAt(i) - 'a'] == null) {
                return false;
            }
            node = node.children[prefix.charAt(i) - 'a'];
        }

        return node.preCount > 0;
    }

    /**
    * 前缀树节点
    */
    private class TrieNode {
        // count代表单词的个数
        int count;
        // preCount代表字符出现的次数
        int preCount;
        TrieNode[] children;

        TrieNode() {
            count = 0;
            preCount = 0;
            children = new TrieNode[26];
        }
    }
}

复杂度分析

  • 时间复杂度:\(O(word.length)\),其中 N 为数组长度。
  • 空间复杂度:\(O(26^N)\),其中 26 意思是最多有26个英文字母, N 即为数组长度
posted @ 2020-12-13 00:24  linzeliang  阅读(146)  评论(0)    收藏  举报