力扣208题前缀树Trie

问题

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

示例:

Trie trie = new Trie();

trie.insert("apple");
trie.search("apple");   // 返回 true
trie.search("app");     // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");   
trie.search("app");     // 返回 true

说明:

你可以假设所有的输入都是由小写字母 a-z 构成的。
保证所有输入均为非空字符串。

基础知识

字典树,又称前缀树,是 N 叉树的特殊形式。通常来说,一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个字符串(前缀)。每一个节点会有多个子节点,通往不同子节点的路径上有着不同的字符。子节点代表的字符串是由节点本身的原始字符串 ,以及通往该子节点路径上所有的字符组成的。

前缀树的一个重要的特性是,节点所有的后代都与该节点相关的字符串有着共同的前缀。这就是前缀树名称的由来。

我们再来看这个例子。例如,以节点 “b” 为根的子树中的节点表示的字符串,都具有共同的前缀 “b”。反之亦然,具有公共前缀 “b” 的字符串,全部位于以 “b” 为根的子树中,并且具有不同前缀的字符串来自不同的分支。

前缀树有着广泛的应用,例如自动补全,拼写检查等等

搜索字典项目的方法为:

  1. 从根结点开始一次搜索;
  2. 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;
  3. 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
  4. 迭代过程……
  5. 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。

其他操作类似。
trie的一般结构形式为:

struct TrieNode {
    
    
    bool isEnd; //该结点是否是一个串的结束
    TrieNode* next[NUM]; //键的值序列表
};

这里的键是指:一个字母就是一个键,如“word”中键长为4

例如:这里的NUM为26,字符串由26个小写字母组成,一个包含四个单词"word",“world”,“work”,"worded"的trie长这样:

在这里插入图片描述
其中的红点表示isEnd为true,简化表示后就是这样:
在这里插入图片描述

解题

public class Trie 
{
    
    
    /** Initialize your data structure here. */
    class TrieNode
    {
    
    
        private boolean isEnd;
        private TrieNode[] next;

        public TrieNode()
        {
    
    
            isEnd = false;
            next = new TrieNode[26];        //每个节点至多有26个小写字母
        }
    }

    private static TrieNode root;

    Trie()
    {
    
    
        root = new TrieNode();
    }

    /**
     * Inserts a word into the trie.
     * @param    word    全部由小写字母组成的字符串
     *
     */
    public void insert(String word) 
    {
    
    
        TrieNode cur = root;
        
        for (int i = 0;i<word.length();i++)
        {
    
    
            int index = word.charAt(i) - 'a';           //计算next中的下标,确定字母
            if (cur.next[index] == null)
            {
    
    
                cur.next[index] = new TrieNode();     //将word的每一个字母创建一个TrieNode,并插入正确的TrieNode数组中
            }
            cur = cur.next[index];
        }
        cur.isEnd = true;
    }

    /** Returns if the word is in the trie. */
    public boolean search(String word) 
    {
    
    
        TrieNode cur = root;
        
        for (int i = 0;i<word.length();i++)
        {
    
    
            int index = word.charAt(i) - 'a';
            if (cur.next[index] == null)
            {
    
    
                return false;
            }
            cur = cur.next[index];
        }
        return cur.isEnd;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) 
    {
    
    
        TrieNode cur = root;
        
        for (int i = 0;i<prefix.length();i++)
        {
    
    
            int index = prefix.charAt(i) - 'a';
            if (cur.next[index] == null)            //如果未遍历完出现next中的值为空则返回false,遍历完说明prefix在前缀树中存在
            {
    
    
                return false;
            }
            cur = cur.next[index];
        }
        return true;
    }

    public static void main(String[] args) {
    
    
        Trie trie = new Trie();
        trie.insert("apple");
        System.out.println(trie.search("apple")); // 返回 true
        System.out.println(trie.search("app"));     // 返回 false
        System.out.println(trie.startsWith("app")); // 返回 true
        trie.insert("app");
        System.out.println(trie.search("app")); // 返回 true
    }
}

复杂度分析

要对长度为n的字符串进行操作
insert: O(n)级时间复杂度
search: O(n)级时间复杂度
空间复杂度为 26n,但是在处理大量数据的时候,实际产生的空间要小于将每个字符串单个存储的大小。

猜你喜欢

转载自blog.csdn.net/weixin_44223946/article/details/109299426
今日推荐