力扣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,但是在处理大量数据的时候,实际产生的空间要小于将每个字符串单个存储的大小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
最长公共前缀是指一组字符串中,所有字符串都有的最长的前缀。根据提供的代码,可以看出这是一个用C语言实现的求解最长公共前缀的函数。函数的输入是一个字符串数组和数组的大小,输出是一个字符指针,指向最长公共前缀的起始位置。 这个函数的实现思路是,首先取数组中第一个字符串的第一个字符作为初始比较字符,然后遍历数组中的每个字符串的相同位置的字符,如果有不相同的字符,则退出循环。如果所有字符串的相同位置的字符都相同,则将初始比较字符更新为下一个位置的字符,继续比较下一位置的字符,直到遍历完所有字符串或者找到不相同的字符。最后,将最长公共前缀的末尾字符置为'\0',返回最长公共前缀的起始位置。 这个函数的时间复杂度是O(n*m),其中n是字符串数组的大小,m是最长公共前缀的长度。 #### 引用[.reference_title] - *1* *2* [力扣:最长公共前缀(详解)](https://blog.csdn.net/weixin_73142957/article/details/129778838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [力扣——14. 最长公共前缀——最长公共前缀C语言解法](https://blog.csdn.net/qq_42479987/article/details/116953103)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

脸是真的白

如果对你有用的话,可以支持一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值