详解字典树原理,代码分析leetcode208. 实现 Trie (前缀树)

news2024/11/28 3:37:01

0、引言

        本文介绍一种能够偶快速查找字符串的树形数据结构-----字典树介绍其原理,以及通过leetcode208题目这个实例,用数组动手实现一棵字典树,并完成其增、查字符串、查字符串前缀的功能。

1、字典树的应用场景

        询问一个单词b,问b是否出现在n个给出的单词中,你会如何去求呢?暴力搜索显然复杂度太高,我们可以把问题转换成查字典的操作:平时是怎么查字典的呢?

        如果你要在字典中查找单词“Avalon”,你是不是先找到首字母为‘A’的部分,然后再找第二个单词为‘V’的部分······最后,你可能可以找到这个单词,当然,也有可能这本词典并没有这个单词。你想想看,你这样子的查单词的方式是不是比你从词典第一页开始查询到最后一页寻找单词“Avalon”要高效多了?那我们可不可以也建一本"字典"呢?
 

2、字典树的介绍

        字典树(TrieTree),是一种树形结构,典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串,如01字典树)。主要思想是利用字符串的公共前缀来节约存储空间。很好地利用了串的公共前缀,节约了存储空间。字典树主要包含两种操作,插入查找。

        在这里,我们借用大佬http://t.csdn.cn/Z1rt2文章的图片例子来说明其原理。
        比如,我们要怎么用树存下单词"abc",“abb”,“bca”,"bc"呢?见图

在这里插入图片描述

         在图中,红点代表有一个以此节点为终点的单词。然后,我们如果要查找某个单词如s=“abc”,就可以这样

在这里插入图片描述

 

        在这里,s=“abc” 的每一个字母都在树中被查到了,并且最后一个点是红色代表有一个在此结束的单词,查询成功。而 s=“bb” 的第二个字母没有在相应位置被查到,因此"bb"不在字典中。至于s=“ab” 虽然单词中每个点都被查到了,但是由于结尾的字母在树中没有标红,因此也是不在字典中。时间复杂度为log级别,比暴力快多了。        

3、字典树的代码实现(leetcode208. 实现 Trie (前缀树)

        力扣有一道现成的实现前缀树的题目,其中包含了初始化前缀树、往前缀树插入某单词、搜索前缀树是否存在某单词、搜索前缀树是否存在某前缀,这四个模块,用这个例子下面我们来实现一下这个字典树。

 3.1 初始化

        对于每个前缀树Trie类的对象,该对象有两个属性:
(1)类型是Trie的大小是26的数组children,象征着26个字母。
        比如,如果存入一个‘b’,那么就在'b' - 'a'的位置,即索引1的位置创建一个Trie类的对象即可。
         用这种方式来标识是否存在某个字母👆
(2)布尔类型的变量isEnd
        因为后面需要判断是否存在“完整单词”与“单词前缀”,因此要设立isEnd,来标志是否是叶子节点。
        比如插入了aabbc,如果搜索aabbc,沿着树找到c,发现isEnd是true,说明存在这个单词。如果搜aabb,能找到b,但isEnd是false,因此不存在这个单词,仅仅存在单词前缀。

代码如下:

//1、初始化26个字母节点数组、2、判断是否为叶子节点的标志
    private Trie[] children;
    private boolean isEnd;
    //每个Trie类的对象有两个属性:
    public Trie() {
        children = new Trie[26];
        isEnd = false;//初始化为非叶子节点,后面如果判断为叶子、再设为true
    }

3.2 插入字符串单词

        插入单词,首先我们要拿到插入单词的Trie对象。
        然后对于插入的word,遍历其每一个字母,在前缀树里往深处添加。代码很容易理解:

public void insert(String word) {
        Trie node = this;//拿到要插入单词的前缀树对象node
        for(int i = 0; i < word.length(); i++){
            char ch = word.charAt(i);
            int index = ch - 'a';
            if(node.children[index] == null){
                node.children[index] = new Trie();
            }
            node = node.children[index];//无论是否为null,node都指向下一层Trie节点
        }
        node.isEnd = true;//最后一层,则为True
    }

3.3 在前缀树中搜索单词

        我们在前缀树里,搜索某个单词,例如在aabb里:搜索ac,那么查到c的时候,children的位置应该是个空值,返回值是null。
        如果搜索aab,那就是虽然能搜到,但isEnd是false。
        如果搜索aabb,不仅能搜到,而且isEnd为true。

        因此,我们可以写一个函数,返回的就是搜索前缀树的时候,搜索到最深处的对象node。
        
这样,如果node为空,就是不存在。node不为空:根据isEnd判断是否是完整单词or前缀单词即可!

代码如下:

    //查是否含有某个完整单词的函数:
    public boolean search(String word) {
        Trie node = searchPrefix(word);
        if(node == null || !node.isEnd){//根本没这个单词,或者,虽然含有有这个单词但不是完整的
            return false;
        }else{
            return true;
        }
        //比如在aapp里搜索ab或者aap,那就是没这个单词
    }
    
    //查是否有某前缀的函数:
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;//只要存在就行,不是结尾也没关系。
        //比如再aaple里搜索aap,那就是有这个前缀。
    }

    //核心函数!!!
    private Trie searchPrefix(String word){
        Trie node = this;
        for(int i = 0; i < word.length(); i++){
            char ch = word.charAt(i);
            int index = ch - 'a';
            if(node.children[index] == null){
                return null;
            }
            node = node.children[index];
        }
        return node;
    }

4、完整代码

class Trie {
    //1、初始化26个字母节点数组、2、判断是否为叶子节点的标志
    private Trie[] children;
    private boolean isEnd;
    //每个Trie类的对象有两个属性:
    public Trie() {
        children = new Trie[26];
        isEnd = false;//初始化为非叶子节点,后面如果判断为叶子、再设为true
    }
    
    public void insert(String word) {
        Trie node = this;
        for(int i = 0; i < word.length(); i++){
            char ch = word.charAt(i);
            int index = ch - 'a';
            if(node.children[index] == null){
                node.children[index] = new Trie();
            }
            node = node.children[index];//无论是否为null,node都指向下一层Trie节点
        }
        node.isEnd = true;//最后一层,则为True
    }
    
    public boolean search(String word) {
        Trie node = searchPrefix(word);
        if(node == null || !node.isEnd){//根本没这个单词,或者,虽然含有有这个单词但不是完整的
            return false;
        }else{
            return true;
        }
        //比如在aapp里搜索ab或者aap,那就是没这个单词
    }
    
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;//只要存在就行,不是结尾也没关系。
        //比如再aaple里搜索aap,那就是有这个前缀。
    }

    private Trie searchPrefix(String word){
        Trie node = this;
        for(int i = 0; i < word.length(); i++){
            char ch = word.charAt(i);
            int index = ch - 'a';
            if(node.children[index] == null){
                return null;
            }
            node = node.children[index];
        }
        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);
 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mujoco 加载机器人模型(三)

目录 .1 简介 1.1 urdf概述 ​编辑 1.2 导出urdf为可用的xml​编辑 1.3 导出测试​编辑 .2 修改 2.1 添加平面和物体 2.2 关节修改 2.2.1 group 2.2.2 关节修改 2.2.3 关节 解压提供的ur5后 修改compiler的 meshdir路径 <mujoco model"ur5"><compi…

一条耗时100ms的SQL把系统搞崩了

一个项目上线了两个月&#xff0c;除了一些反馈的优化和小Bug之外&#xff0c;项目一切顺利。前期是属于推广阶段&#xff0c;可能使用人员没那么多&#xff0c;当然对于项目部署肯定提前想到并发量了&#xff0c;所以早就把集群安排上&#xff0c;而且还在测试环境搞了一下压测…

2023年的深度学习入门指南(15) - 大模型的幻觉

2023年的深度学习入门指南(15) - 大模型的幻觉 大模型的能力最另人惊讶的&#xff0c;一个是强大的能力&#xff0c;另一个就是时不时一本正经地胡说八道。如果你用的是小一点的模型&#xff0c;可能还见过输出循环内容之类的情况。我们将这种生成不良内容的现象称为幻觉-hall…

3d动画用云渲染靠谱吗?

3d动画是一种利用计算机技术制作的动画形式&#xff0c;它可以模拟真实世界的物体和场景&#xff0c;创造出各种惊人的效果和视觉体验。3d动画广泛应用于影视、游戏、广告、教育等领域&#xff0c;成为当今最流行的艺术表现形式之一。据统计&#xff0c;2019年全球3d动画市场规…

吴恩达老师《机器学习》课后习题1之线性回归

在学习这些内容之前&#xff0c;需要学习python数据分析相关内容&#xff1a; numpy&#xff1a;科学计算库&#xff0c;处理多维数组&#xff0c;进行数据分析 pandas&#xff1a;基于numpy的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的 matplotlib&#xff1a…

Atcoder Beginner Contest 297

A - Double Click AC代码&#xff1a; #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N110; int t[N]; int main() {int n,d;cin>>n>>d;for(int i1;i<n;i) cin>>t[i];bool flagfalse;i…

数据结构05:树的定义与双亲表示法[持续更新中]

参考用书&#xff1a;王道考研《2024年 数据结构考研复习指导》 参考用书配套视频&#xff1a;5.1.1 树的定义和基本术语_哔哩哔哩_bilibili 特别感谢&#xff1a; Chat GPT老师[部分名词解释、修改BUG]、BING老师[封面图]~ 备注&#xff1a;博文目前是未完成的状态&#xff…

【数据结构】哈希表(Map和Set)

文章目录 Map和Set模型MapTreeMap和HashMap对比Entry<K,V>常用方法 SetTreeSet和HashSet对比常用方法 OJ练习只出现一次数字复制带随机指针的链表宝石与石头坏键盘打字前K个高频单词 哈希表哈希表所用数据结构解决哈希冲突闭散列开散列 避免哈希冲突哈希函数设计负载因子…

springboot bean的生命周期

Spring Boot是一个非常流行的Java框架&#xff0c;它提供了许多功能&#xff0c;使开发人员可以快速构建和部署应用程序。其中一个非常重要的功能是Spring Boot Bean的生命周期。在本文中&#xff0c;我们将深入探讨Spring Boot Bean的生命周期&#xff0c;以及如何最大化利用它…

光伏发电系统最大功率跟踪控制MATLAB仿真模型(电导增量法+扰动观察法)

光伏发电系统最大功率跟踪控制MATLAB仿真模型&#xff08;电导增量法扰动观察法参考文献&#xff09;资源-CSDN文库https://download.csdn.net/download/weixin_56691527/87878528 模型介绍&#xff1a; 模型主要包含光伏电池模块、直流升压模块、以及最大功率跟踪控制模块。…

深度学习--神经网络全面知识点总结(持续更新中)

文章目录 神经网络基础1.1 什么是神经网络&#xff1f;1.2 神经元和激活函数1.3 前向传播和反向传播1.4 损失函数和优化算法 深度神经网络2.1 卷积神经网络&#xff08;CNN&#xff09;2.2 循环神经网络&#xff08;RNN&#xff09;2.3 长短期记忆网络&#xff08;LSTM&#xf…

C#可视化 商品信息管理系统(具体做法及全部代码)

目录 题目&#xff1a; 效果图&#xff1a; 数据库&#xff1a; 做法&#xff1a; 主页面添加menustrip&#xff0c;之后添加两个窗体双击事件&#xff0c;双击下拉区域就好了。 添加界面 查询按钮功能 datagirdview设置 全部代码&#xff1a; DBHelper类 From1主窗体代…

【并发篇】01 java中的线程状态

Java线程分为6种状态&#xff1a; &#xff08;1&#xff09;新建NEW&#xff1a;用new关键字创建的线程就是新建状态&#xff0c;这时候还没有和系统底层真正的线程关联起来&#xff0c;还仅仅只是一个java对象&#xff0c;所以这个时候这个线程不会被系统分配给cpu。 &#…

【云原生】docker

容器化越来越受欢迎&#xff0c;因为容器是&#xff1a; ●灵活&#xff1a;即使是最复杂的应用也可以集装箱化。 ●轻量级&#xff1a;容器利用并共享主机内核。 ●可互换&#xff1a;可以即时部署更新和升级。 ●便携式&#xff1a;可以在本地构建&#xff0c;部署到云&#…

shel脚本基础1——变量基础、脚本运算符

文章目录 一、变量基础二、bash变量类型三、脚本基础知识3.1 脚本测试命令3.2 shell运算符3.3 命令逻辑关系3.4 bash条件判断3.4.1 条件测试的表达式3.4.2 整数测试3.4.2 字符测试3.4.3 文件测试3.4.4 组合测试条件 一、变量基础 变量的含义&#xff1a; 变量可以通过变量名访问…

5.数据结构期末复习之图以及相关算法

1.应用: 教学计划(先修什么才能修什么课程)社交网络地图导航 2.什么是图? 顶点(有穷非空个)顶点的边 ,抽象为G(V,E) (Vert,Edge) 1.无向图: 点到点都可以到达 表示为(vi,vj) 2.有向图 只能一个点到另外一个点 表示为<vi,vj> 又分为: 1.非带权图(上面的例子就是) 2.带权图…

Day29

TCP_SER #include <myhead.h>//定义处理错误的宏函数 #define ERR_MSG(msg) do{\fprintf(stderr,"line:%d %s %s",__LINE__, __FILE__, __func__);\perror(msg);\ }while(0)//定义IP和端口号 #define IP "192.168.2.186" #define PORT 6666int …

数组的定义方式及访问

问题 如何创建及访问数组。 方法 了解数组的概念 数组就是存储多个数据的容器&#xff0c;数组的长度固定&#xff0c;多个数据的数据类型要一致。 数组的三种定义方式 数据存储的数据类型[] 数组名字 new 数组存储的数据类型[长度] 数据类型[] 数组名 new 数据类型[]{元素…

测试人35岁何去何从?软件测试路在何方?“我“一路升级打怪...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 测试员干到35岁&a…

暑期健身房招生活动文案分享,在线传单设计

想要有效地宣传健身房的信息&#xff0c;还能够吸引学员了解报名的宣传单要怎么制作&#xff1f;不用自己动手设计&#xff0c;借助在线模板和在线设计工具&#xff0c;一键就能生成专属的招生传单。以下是在线招生传单设计教程和传单设计模板使用教程。 首先找到一个在线设计工…