字典树/前缀树Trie(附Java代码)

news2024/9/22 15:36:24

字典树/前缀树

  • 1.字典树Trie
    • 1.1 字典树举例
  • 2.代码实现
    • 2.0 代码结构
    • 2.1方式一:使用数组存储孩子节点
    • 2.2方式二:使用HashMap存储孩子节点

在计算机科学中,trie,又称前缀树字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。Trie这个术语来自于retrieval(检索)。根据词源学,trie的发明者Edward Fredkin把它读作/ˈtriː/ “tree”。但是,其他作者把它读作/ˈtraɪ/ “try”。
trie中的通常是字符串,但也可以是其它的结构。trie的算法可以很容易地修改为处理其它结构的有序序列,比如一串数字或者形状的排列。比如,bitwise trie中的键是一串比特,可以用于表示整数或者内存地址。

1.字典树Trie

  • 只有一个根节点,且根节点本身不存储任何键数据;
  • 每个节点拥有不确定数目的孩子节点,具体取决于由该节点可以发散出多少分支,比如a开头的单词都可以从a出发,也就是作为a的子孙节点;
  • 当从根节点到当前节点的路径所构成的单词为合法单词,则需进行如下工作:1)标记当前节点为“终止”节点,表示根节点到此节点可表示一个完整单词,该节点并不一定是叶子结点,也有可能为内部节点,比如app和apple在字典树中的体现;2)将完整单词记录在当前节点中;
  • 字典树常用于解决字符串前缀匹配问题、词频统计问题;

1.1 字典树举例

比如有四个单词:app、apple、bit、cat,根据他们构建字典树如下:
在这里插入图片描述

2.代码实现

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

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

2.0 代码结构

Trie代码实现中主要涉及四个内容:

  • root节点,本身不存储任何内容,拥有孩子节点;
  • 孩子节点可能有多个,可使用数组(明确孩子节点的数量)或哈希表(孩子节点的数量未知)进行存储,孩子节点本身也是一个Trie树,与root节点不同的是,该节点存储对应的键值;
  • 如果到达某个节点时,可表示一个完整的合法单词,则将其标记为“终止”节点,用于字典树查找操作;
  • 也可在终结节点处记录完整单词的内容;

2.1方式一:使用数组存储孩子节点

  • 此处假设所有单词只有小写字母组成,所以每个节点的孩子节点数目最多为26个(其实有些浪费空间);
  • 在如上假设下,某个节点的孩子节点中,children[ch-‘a’]则表示以当前字符ch为键的孩子节点;
    class Trie {
    	// 存储孩子节点
        Trie[] children;
        // 标记该节点是否为终止节点
        boolean isEnd;
        public Trie() {
        	// 初始化
            children=new Trie[26];
        }

		// 数据插入操作
        public void insert(String word) {
            // 当前根节点
            Trie cur=this;
            // 当前根节点的孩子节点
            Trie[] branches=cur.children;
            char[] chs=word.toCharArray();
            // 依次遍历当前单词的所有字符
            for(int i=0;i<chs.length;i++){
//                int index=chs[i]-'a';
//                if(branches[index]!=null){
//                    cur=branches[index];
//                    branches=cur.children;
//                }else{                // 说明该字符已经存在
//                    branches[index]=new Trie();
//                    cur=branches[index];
//                    branches=cur.children;
//                }
				// 确定孩子节点的位置
                int index=chs[i]-'a';
                // 说明该字符不存在,则对该孩子节点进行初始化
                if(branches[index]==null){
                    branches[index]=new Trie();
                }
                // 调整当前根节点为该孩子节点
                cur=branches[index];
                branches=cur.children;
            }
            // 单词遍历结束,此时cur指向该单词最后一个字符所在节点,将其标记为终止节点
            cur.isEnd=true;
        }
		
		// 
        public boolean search(String word) {
            // 当前根节点
            Trie cur=this;
            Trie[] branches=children;
            char[] chs=word.toCharArray();
            for(int i=0;i<chs.length;i++){
                int index=chs[i]-'a';
				// 如果该字符存在,则一直向下搜索,直到遍历完整个单词
                if(branches[index]!=null){
                    cur=branches[index];
                    branches=cur.children;
                }else{ // 说明该字符不存在
                    return false;
                }
            }
            // 此时cur指向单词的最后一个字符对应的节点,如果该节点是终止节点则说明该单词存在
            return cur.isEnd;
        }

        public boolean startsWith(String prefix) {
            // 当前根节点
            Trie cur=this;
            Trie[] branches=cur.children;
            char[] chs=prefix.toCharArray();
            for(int i=0;i<chs.length;i++){
                int index=chs[i]-'a';
                // 说明该字符已经存在
                if(branches[index]!=null){
                    cur=branches[index];
                    branches=cur.children;
                }else{
                    return false;
                }
            }
            // 此时cur指向单词的最后一个字符对应的节点,说明整个字符串都存在,也就是以该字符串开头的单词都存在
            return true;
        }
    }

2.2方式二:使用HashMap存储孩子节点

class Trie {
        HashMap<Character,Trie> children;
        boolean isEnd;
        String value;
        public Trie() {
            children=new HashMap<>();
        }

        public void insert(String word) {
            Trie cur=this;
            HashMap<Character,Trie> branches=cur.children;
            char[] chs=word.toCharArray();
            for(int i=0;i<chs.length;i++){
                if(!branches.containsKey(chs[i])){
                    branches.put(chs[i],new Trie());
                }
                cur=branches.get(chs[i]);
                branches=cur.children;
            }
            cur.isEnd=true;
            cur.value=word;
        }

        public boolean search(String word) {
            Trie cur=this;
            HashMap<Character,Trie> branches=cur.children;
            char[] chs=word.toCharArray();
            for(int i=0;i<chs.length;i++){
                if(branches.containsKey(chs[i])){
                    cur=branches.get(chs[i]);
                    branches=cur.children;
                }else{
                    return false;
                }
            }
            return cur.isEnd;
        }

        public boolean startsWith(String prefix) {
            Trie cur=this;
            HashMap<Character,Trie> branches=cur.children;
            char[] chs=prefix.toCharArray();
            for(int i=0;i<chs.length;i++){
                if(branches.containsKey(chs[i])){
                    cur=branches.get(chs[i]);
                    branches=cur.children;
                }else{
                    return false;
                }
            }
            return true;
        }
    }

参考资料:

  • 爱学习的饲养员;
  • 维基百科;

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

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

相关文章

一文带你上高速入门linux(含常用命令语法、说明、举例)

一文带你入门linux常用命令&#xff08;含举例和详细说明&#xff09; Linux系统操作介绍1. Linux发行版2. Linux文件系统3. Linux基本命令4. Linux用户和权限管理5. 软件包管理6. 系统管理7. 网络管理8. Shell脚本9. 文本处理10. 系统监控和日志11. 总结 Linux系统操作介绍 L…

塔望3W消费战略全案丨品类重新定义 打造金皇品高端速食第一面

金皇品 客户&#xff1a;上海皇品食品有限公司 品牌&#xff1a;金皇品 服务&#xff1a;3W消费战略 品牌全案 项目背景 金皇品隶属于上海皇品食品有限公司&#xff0c;品牌创立于1995年。产品以原汁作为核心卖点&#xff0c;通过线下渠道的密集耕耘&#xff0c;在浙江&…

NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)

主篇 npm 上传发布自定义组件以及使用详细流程。 npm login 报错&#xff1a;Unexpected token < in JSON at position 0 while parsing near ‘<!DOCTYPE HTML PUBLI…’。 登录 npm $ npm login Username: dengzemiao Password: Email: (this IS public) xxxx163.co…

【李宏毅】GNN学习笔记

视频链接 [TA 補充課 Graph Neural Network (1/2) (由助教姜成翰同學講授) - YouTube] [TA 補充課 Graph Neural Network (2/2) (由助教姜成翰同學講授) - YouTube] [speech.ee.ntu.edu.tw/~tlkagk/courses/ML2020/GNN.pdf] Introduction 应用&#xff1a;分类、Generatio…

Docker 快速入门

1、Docker 简介 Docker是一个开源的容器引擎&#xff0c;它可以帮助我们更快地交付应用。Docker可将应用程序和基础设施层隔离&#xff0c;并且能将基础设施当作程序一样进行管理。使用Docker&#xff0c;可更快地打包、测试以及部署应用程序&#xff0c;并可减少从编写到部署…

mysql主从复制搭建--待实践完善

一、什么是mysql主从复制 参考链接&#xff1a;MySQL主从介绍_rain_yunlx的博客-CSDN博客 Linux下搭建Mysql主从复制详细步骤&#xff08;Mysql版本5.7.35&#xff09;_linuxmysql主从_Direct_的博客-CSDN博客 Linux环境MySQL数据库主从复制保姆级教程_linux主从复制_小学生…

山东大学2023操作系统实验2

目录 (1)操作系统实验2内容: (2)完成方式: (3)使用函数和功能: 1.pipe函数与有关的功能 2.pipe的返回值 3.pipe的参数 4.使用事项: (4)实现代码 (5)实现效果 (1)操作系统实验2内容: 创建三个并行的进程,分别完成计算F(x),F(y),F(x,y)三个函数的计算 (2)完成方式: 实验…

再也不去字节跳动面试了,6年测开经验的真实面试经历.....

前几天我朋友跟我吐苦水&#xff0c;这波面试又把他打击到了&#xff0c;做了快6年软件测试员。。。为了进大厂&#xff0c;也花了很多时间和精力在面试准备上&#xff0c;也刷了很多题。但题刷多了之后有点怀疑人生&#xff0c;不知道刷的这些题在之后的工作中能不能用到&…

18.Java异常

Java异常 Java 中的异常&#xff08;Exception&#xff09;又称为例外&#xff0c;是一个在程序执行期间发生的事件&#xff0c;它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误&#xff0c;Java中引入了异常类的概念。异常类的引入即明确了程序在执…

QT stackUnder 用法小结

stackUnder是什么意思&#xff1f;根据官方的解析&#xff0c;简而言之一句话&#xff1a;把窗口的小部件放置于父窗口的堆栈窗口中。本文将通过一则简单的示例&#xff0c;来具体解析stackUnder该如何使用。 官方解析 void QWidget::stackUnder(QWidget *w) Places the widg…

ThinkPHP6之数据库操作上

ThinkPHP6之数据库操作上 前言1. 数据库配置2. 数据库操作1. 查询操作2. 插入操作3. 修改4. 删除5. 其他 3.数据集总结 前言 注意&#xff0c;tp6在进行语法学习的时候都是在app/index.php中写代码的&#xff0c;代码写在index函数下面&#xff0c;而且tp6自带的文件都是由自动…

经常打电话的人用什么耳机好?通话质量好的蓝牙耳机推荐

用蓝牙耳机听歌、刷视频、玩游戏已经成为趋势&#xff0c;作为人手必备的一件单品&#xff0c;在款式层出不穷&#xff0c;功能各异的蓝牙耳机市场&#xff0c;许多人都不知道究竟什么蓝牙耳机好&#xff1f;下面整理了几款通话质量好的蓝牙耳机。 一、南卡小音舱Lite2蓝牙耳机…

【服务器数据恢复】Raid磁盘阵列常见故障类型原因分析

由于raid的特点和优势&#xff0c;磁盘阵列技术被广泛应用于服务器和存储等商用领域。由于用户基数大&#xff0c;出现故障的情况也不少。通过这篇文章介绍一下常见的raid磁盘阵列数故障类型和原因。 故障类型一、磁盘阵列处于降级状态时未及时rebuild。 RAID磁盘阵列的数据安全…

Linux+云服务器

目录 前言 一、Linux介绍 二、Linux 环境搭建 2.1 云服务器 2.2 XShell 终端 三、Linux 常用命令 3.1操作目录的命令 3.1.1 ls 【list的缩写】 双击某个目录 3.1.2 pwd 【print working directory的缩写】打印当前所处地址 3.1.3 cd 【change directory的缩写】切…

DAY02_运算符和选择语句

1&#xff1a;运算符 对数据进行运算。而对数据运算就要用到运算符。 和表达式做一个简单的描述&#xff1a; 运算符&#xff1a;对字面量或者变量进行操作的符号 表达式&#xff1a;用运算符把字面量或者变量连接起来符合java语法的式子就可以称为表达式。不同运算符连接的…

JUC多并发编程 volatile

特点&#xff1a; 可见性有序性(有时需要禁用重排) 内存语义&#xff1a; 当写一个 volatile 变量时&#xff0c; JMM 会把该线程对应的本地内存中的共享变量值立即刷新回主存中当读一个 volatile 变量时&#xff0c; JMM 会把该线程对应的本地内存设置无效&#xff0c;重新…

m4a怎么转换成mp3,4招搞定

m4a怎么转换成mp3&#xff1f;相信很多朋友都知道&#xff0c;无论是音频、视频、图片等很多电子文件&#xff0c;都会有多种格式。由于格式不统一&#xff0c;很容易导致文件和软件之间出现不兼容的情况&#xff0c;最直接的结果就是在播放器中无法打开&#xff0c;或者使用的…

一篇文章带你了解Spring/SpringBoot常用注解(建议收藏!!! )

这篇文章介绍的 Spring/SpringBoot 常用注解基本已经涵盖你工作中遇到的大部分常用的场景。对于每一个注解我都说了具体用法&#xff0c;掌握搞懂&#xff0c;使用 SpringBoot 来开发项目基本没啥大问题了&#xff01; 1. SpringBootApplication 这里先单独拎出SpringBootAppl…

NUXT规范及常见问题

props中不要使用Web环境才有的对象&#xff0c;服务端渲染的时候会失败 使用<Nuxt/>组件代替<router-view/>&#xff0c;使用<NuxtLink/>代替<router-link/>static目录下的资源是静态资源&#xff0c;不应该通过import或../static/img/logo.png等方式…

【AI绘画】我以Midjourney为主学习AI绘画效果咋样?

上一篇博客链接&#xff1a;【ChatGPT】ChatGPT掀起AIGC与AI浪潮_山楂山楂丸的博客-CSDN博客 这周&#xff0c;我加入了新星计划&#xff0c; 涉及的领域是我感兴趣以及对未来规划有帮助的——AI绘画&#xff01;​​​​​​​ 文章目录 前言 一、AI绘画是什么 二、AI绘画进…