首页 > 编程语言 >LeetCode100之实现Trie前缀树(208)--Java

LeetCode100之实现Trie前缀树(208)--Java

时间:2024-12-22 20:32:33浏览次数:6  
标签:node Java 前缀 Trie 208 单词 word 节点

1.问题描述

Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word 。
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

        示例1

输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]

解释
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

        提示

  • 1 <= word.length, prefix.length <= 2000
  • word 和 prefix 仅由小写英文字母组成
  • insertsearch 和 startsWith 调用次数 总计 不超过 3 * 104 次

        难度等级

                中等

        题目链接

                实现Trie(前缀树)

2.解题思路

        这道实现一个Trie的题目很有意思,我们如果在一个树节点存放一个单词的话,当节点少的时候,我们可以快速的查找到,但是当节点一多起来,查找效率就不怎么样了;而如果我们一个树节点只存放一个单词的字符,但是有26个节点可以来存放26个字符的话,当节点少的时候,查询一个单词需要查询那个单词字符长度那么多个节点,看起来不怎么样,但是当单词一旦多起来,节点并不会根着多特别多,查询一个单词依旧是只需要查询那个单词长度那么多个节点,从这个角度看,效率就高很多了。

        通过上面的比较,我们肯定选择第二种实现方式来完成实现这个Trie树。

        我们先来完成构造方法。我们可以给一个树节点提供一个数组,数组里存放26个子节点来存放26个字母,同时提供一个是否结束的标识,因为我们的Trie树是要即可以查单词,又可以查前缀的,有了结束标识后,我们就可以知道从一开始查询的节点,到当前节点的每一个字符可以拼成一个完整的单词或者前缀;如果没有结束标识,那拼成的就是一个前缀。

    //子树
    private Trie[] children;
    //是否是一个字符串结束
    private boolean isEnd;
    public Trie() {
        children = new Trie[26];
        isEnd = false;
    }

        有了构造方法之后,我们就来完成插入方法。我们的第一个字符要存放在根节点,所以我们要定义一个节点指针,赋值为当前节点。接着,用一个for循环依次将每一个字符取出来,根据字符到节点数组中取对应的下一个节点,如果节点为空,我们就创建一个节点,并将指针指向新创建的节点;如果节点不为空,将指针指向该节点。这样不断的取出字符,往下找节点,直到将整个单词插入到树中。最后,在最后一个节点中结束标识标记为true,插入操作就结束了。

    public void insert(String word) {
        //从当前树存放第一个字符
        Trie node = this;
        //将字符串的字符一个一个取出来,存到一颗一颗的子树中
        for(int i = 0;i < word.length();i++){
            char c = word.charAt(i);
            //对应的前缀树点为空,则创建一颗前缀树
            if(node.children[c - 'a'] == null){
                node.children[c - 'a'] = new Trie();
            }
            //移动指针到下一个节点
            node = node.children[c - 'a'];
        }
        //在最后一个节点标记字符串结束
        node.isEnd = true;
    }

        因为寻找单词和寻找前缀本质都是在Trie树中每个节点对应的字符,所以我们可以把这两个方法相同的部分抽取出来,单独用一个方法来实现,我们就叫做寻找前缀方法,该方法返回所寻找的字符串的最后一个节点。因为单词其实也是一个前缀。

        寻找前缀的方法和插入的方法差不多,从根节点开始,依次取出每一个字符,然后从寻找对应的子节点,如果子节点为空,说明找不到对应的前缀,直接返回空即可;如果子节点不为空,说明找到了当前字符,将节点指针指向找到的这个节点,在这个节点的子节点中寻找下一个字符。当遍历完整个字符串之后,将最后一个节点返回即可。

    public Trie searchPrefix(String prefix){
        //从当前节点开始寻找
        Trie node = this;
        for(int i = 0;i < prefix.length();i++){
            //取出字符
            char c = prefix.charAt(i);
            if(node.children[c - 'a'] == null){
                //找不到,返回空
                return null;
            }
            //找到了,移动到下一个节点,寻找下一个字符
            node = node.children[c - 'a'];
        }
        //将最后一个节点返回
        return node;
    }

        然后我们回到查找单词的方法,我们只需要调用寻找前缀的方法,就可以得到前缀的最后一个节点,如果节点不为空而且节点的结束标识为true,说明有这么一个单词。

    public boolean search(String word) {
        //将单词当做前缀去寻找,找到的最后一个节点如果包含结束标识,说明是一个单词
        Trie node = searchPrefix(word);
        return  node != null && node.isEnd; 
    }

        最后回到查找前缀的方法,其实我们只需要调用寻找前缀的方法即可,如果返回的节点不为空,说明前缀存在;未空,说明前缀不存在。

    
    public boolean startsWith(String prefix) {
        //寻找前缀时,返回的节点不为空,说明找到该前缀
        return searchPrefix(prefix) != null;
    }

3.代码展示

class Trie {
    //子树
    private Trie[] children;
    //是否是一个字符串结束
    private boolean isEnd;
    public Trie() {
        children = new Trie[26];
        isEnd = false;
    }
    
    public void insert(String word) {
        //从当前树存放第一个字符
        Trie node = this;
        //将字符串的字符一个一个取出来,存到一颗一颗的子树中
        for(int i = 0;i < word.length();i++){
            char c = word.charAt(i);
            //对应的前缀树点为空,则创建一颗前缀树
            if(node.children[c - 'a'] == null){
                node.children[c - 'a'] = new Trie();
            }
            //移动指针到下一个节点
            node = node.children[c - 'a'];
        }
        //在最后一个节点标记字符串结束
        node.isEnd = true;
    }
    
    public boolean search(String word) {
        //将单词当做前缀去寻找,找到的最后一个节点如果包含结束标识,说明是一个单词
        Trie node = searchPrefix(word);
        return  node != null && node.isEnd; 
    }
    
    public boolean startsWith(String prefix) {
        //寻找前缀时,返回的节点不为空,说明找到该前缀
        return searchPrefix(prefix) != null;
    }
    public Trie searchPrefix(String prefix){
        //从当前节点开始寻找
        Trie node = this;
        for(int i = 0;i < prefix.length();i++){
            //取出字符
            char c = prefix.charAt(i);
            if(node.children[c - 'a'] == null){
                //找不到,返回空
                return null;
            }
            //找到了,移动到下一个节点,寻找下一个字符
            node = node.children[c - 'a'];
        }
        //将最后一个节点返回
        return node;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

4.总结

        这道题是叫我们实现一个高效查找单词和前缀的Trie,需要我们对数据结构有一点了解和经验。这里我们提供的思路是每一个节点有26个子节点,但是每一个单词在该节点只存放一个的字符,这样前缀相同的单词,可以复用相同的节点,用一个是否为单词的标记来提醒截止该节点,是否是一个存储在其中的完整单词。

        今天这道题就啰嗦到这,祝大家刷题愉快~

标签:node,Java,前缀,Trie,208,单词,word,节点
From: https://blog.csdn.net/2301_79318558/article/details/143807389

相关文章

  • 软硬皆施,助力2025Java面试
    前言我们都知道,现代面试的核心在于考察项目经验、基本技术能力以及个人发展潜力。每一次面试,都是对我们综合能力的一次全面检验,这不仅包括硬技能,也涵盖了软实力的展现。软实力主要体现在简历的精心包装、流畅自信的自我介绍、与面试官的沟通技巧等方面;而硬实力则是我们技术......
  • JAVA没有搞头了吗?
    前言今年的Java程序员群体似乎承受着前所未有的焦虑。投递简历无人问津,难得的面试机会也难以把握,即便成功入职,也往往难以长久。于是,不少程序员感叹:互联网的寒冬似乎又一次卷土重来,环境如此恶劣,努力似乎也变得无足轻重,不如选择躺平。然而,真相果真如此吗?实际上,“寒冬”始终......
  • java + mysql 023Java+学生宿舍管理系统的设计与开发录像(完整源码 + 说明文档 + 演示
     ......
  • java + mysql 024Java+基于SpringBoot的企业客户管理系统录像(完整源码 + 说明文档 +
     ......
  • Java:为什么容器接口中定义的clear()方法具体实现要遍历每个元素并将其设置为null,而不
    以ArrayList为例,其clear()的具体实现为遍历每一个元素,并将其设置为null。publicvoidclear(){modCount++;finalObject[]es=elementData;for(intto=size,i=size=0;i<to;i++)es[i]=null;}笔者作为初学者,很难不产生疑惑,为什么不将s......
  • java-io流
    根据流的方向:输入流(InputStream/Reader):从数据源读取数据到程序中。输出流(OutputStream/Writer):将数据从程序写入到目的地。根据流处理信息的大小:字节流(ByteStreams):以字节为单位处理数据,适用于所有类型的数据传输,如二进制文件。字符流(CharacterStreams):以字符为单位处理数据......
  • 基于Java的班级管理系统的设计与实现 毕业设计-附源码60085
    摘要班级管理是学校管理的重要组成部分,传统的班级管理方式存在效率低下、信息不及时等问题。为了解决这些问题,本文设计并实现了一个基于 Java 的班级管理系统。 本论文旨在设计并实现一个基于 Java 的班级管理系统,以提高班级管理的效率和准确性。该系统采用了SSM框架......
  • 深刻理解JAVA8新特性
    相信每一位java面试者都会问一个问题,你知道jdk1.8新特性吗?当在回答这个问题的时候,我们都会说一大堆,比如说,可以支持lambda表达式,引入Optional类让开发开始手动检查null,避免运行时候的NPE等等,可是,对于一名java从业者来说,我觉得不够,没有说到点子上,如果面试官很水,当然就让你过啦,然......
  • springboot-Java搭建的后端服务器返回前端请求结果
    访问spring.io,在上方的projects找到springInitializr,配置如下:点击下方的GENERATE下载。解压到你的workspace文件夹,然后将该位置复制,IDEA中点击左上角->打开,粘贴文件地址,选中springboottest根目录,确定。此时点信任,信任该文件夹,打开新窗口。还是左上角->setting,搜索Maven,配置......
  • Java程序打包成exe,无Java环境也能运行
    Java程序开发完成后,通常情况下以jar包的形式发布。但有时我们需要给非软件开发人员使用程序,如制作好窗体应用,把它发给没有java开发环境的人使用,此时就需要制作exe安装包。本文介绍如何将java程序制作成exe安装包,并提供有图片和三方依赖jar包的解决方案。1.安装exe制作软件制......