算法 | 动画+解析,轻松理解「Trie树」
Trie这个名字取自“retrieval”,检索,因为Trie可以只用一个前缀便可以在一部字典中找到想要的单词。
虽然发音与「Tree」一致,但为了将这种 字典树 与 普通二叉树 以示区别,程序员小吴一般读「Trie」尾部会重读一声,可以理解为读「TreeE」。
Trie 树,也叫“字典树”。顾名思义,它是一个树形结构。它是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。
此外 Trie 树也称前缀树(因为某节点的后代存在共同的前缀,比如pan是panda的前缀)。
它的key都为字符串,能做到高效查询和插入,时间复杂度为O(k),k为字符串长度,缺点是如果大量字符串没有共同前缀时很耗内存。
它的核心思想就是通过最大限度地减少无谓的字符串比较,使得查询高效率,即「用空间换时间」,再利用共同前缀来提高查询效率。
Trie树的特点
假设有 5 个字符串,它们分别是:code,cook,five,file,fat。现在需要在里面多次查找某个字符串是否存在。如果每次查找,都是拿要查找的字符串跟这 5 个字符串依次进行字符串匹配,那效率就比较低,有没有更高效的方法呢?
如果将这 5 个字符串组织成下图的结构,从肉眼上扫描过去感官上是不是比查找起来会更加迅速。
通过上图,可以发现 Trie树 的三个特点:
根节点不包含字符,除根节点外每一个节点都只包含一个字符
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
每个节点的所有子节点包含的字符都不相同
通过动画理解 Trie 树构造的过程。在构造过程中的每一步,都相当于往 Trie 树中插入一个字符串。当所有字符串都插入完成之后,Trie 树就构造好了。
Trie树的插入操作
Trie树的插入操作很简单,其实就是将单词的每个字母逐一插入 Trie树。插入前先看字母对应的节点是否存在,存在则共享该节点,不存在则创建对应的节点。比如要插入新单词 cook,就有下面几步:
插入第一个字母 c,发现 root 节点下方存在子节点 c,则共享节点 c
插入第二个字母 o,发现 c 节点下方存在子节点 o,则共享节点 o
插入第三个字母 o,发现 o 节点下方不存在子节点 o,则创建子节点 o
插入第三个字母 k,发现 o 节点下方不存在子节点 k,则创建子节点 k
至此,单词 cook 中所有字母已被插入 Trie树 中,然后设置节点 k 中的标志位,标记路径 root->c->o->o->k 这条路径上所有节点的字符可以组成一个单词cook
Trie树的查询操作
在 Trie 树中查找一个字符串的时候,比如查找字符串 code,可以将要查找的字符串分割成单个的字符 c,o,d,e,然后从 Trie 树的根节点开始匹配。如图所示,绿色的路径就是在 Trie 树中匹配的路径。
如果要查找的是字符串 cod(鳕鱼)呢?还是可以用上面同样的方法,从根节点开始,沿着某条路径来匹配,如图所示,绿色的路径,是字符串 cod 匹配的路径。但是,路径的最后一个节点「d」并不是橙色的,并不是单词标志位,所以 cod 字符串不存在。也就是说,cod是某个字符串的前缀子串,但并不能完全匹配任何字符串。
程序员不要当一条咸鱼,要向 cook 靠拢:)
Trie树的删除操作
Trie树的删除操作与二叉树的删除操作有类似的地方,需要考虑删除的节点所处的位置,这里分三种情况进行分析:
删除整个单词(比如hi)
从根节点开始查找第一个字符h
找到h子节点后,继续查找h的下一个子节点i
i是单词hi的标志位,将该标志位去掉
i节点是hi的叶子节点,将其删除
删除后发现h节点为叶子节点,并且不是单词标志位,也将其删除
这样就完成了hi单词的删除操作
删除前缀单词(比如cod)
这种方式删除比较简单。
只需要将cod单词整个字符串查找完后,d节点因为不是叶子节点,只需将其单词标志去掉即可。
删除分支单词(比如cook)
与 删除整个单词 情况类似,区别点在于删除到 cook 的第一个 o 时,该节点为非叶子节点,停止删除,这样就完成 cook 字符串的删除操作。
Trie树的应用
事实上 Trie树 在日常生活中的使用随处可见,比如这个:
具体来说就是经常用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
1. 前缀匹配
例如:找出一个字符串集合中所有以 五分钟 开头的字符串。我们只需要用所有字符串构造一个 trie树,然后输出以 五−>分−>钟 开头的路径上的关键字即可。
trie树前缀匹配常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。
2. 字符串检索
给出 N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,按最早出现的顺序写出所有不在熟词表中的生词。
检索/查询功能是Trie树最原始的功能。给定一组字符串,查找某个字符串是否出现过,思路就是从根节点开始一个一个字符进行比较:
如果沿路比较,发现不同的字符,则表示该字符串在集合中不存在。
如果所有的字符全部比较完并且全部相同,还需判断最后一个节点的标志位(标记该节点是否代表一个关键字)。
Trie树的局限性
如前文所讲,Trie的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
假设字符的种数有 m 个,有若干个长度为n的字符串构成了一个 Trie 树 ,则每个节点的出度为 m(即每个节点的可能子节点数量为 m),Trie 树的高度为 n。很明显我们浪费了大量的空间来存储字符,此时 Trie 树的最坏空间复杂度为 O(m^n)。也正由于每个节点的出度为 m,所以我们能够沿着树的一个个分支高效的向下逐个字符的查询,而不是遍历所有的字符串来查询,此时 Trie 树的最坏时间复杂度为 O(n)。
这正是空间换时间的体现,也是利用公共前缀降低查询时间开销的体现。
(*本文为作者投稿文章,转载请联系作者)
公开课预告
◆
强化学习
◆
本课程是一次理论+实战的结合,将重点介绍强化学习的模型原理以及A3C模型原理,最后通过实践落实强化学习在游戏中的应用。
推荐阅读:
BAT七年经验,却抵不过外企面试的两道算法题?
刘铁岩谈机器学习:随波逐流的太多,我们需要反思
Python爬取爱奇艺腾讯视频250,000条数据分析为什么李诞不值得了?
这个用Python编写的PDF神器你值得拥有!
云计算演进历程与模式 - 初识云计算知识专栏(2)
“iPhone 3 年内必死!”
数据库的 N 多骚操作了解一下?
25k~60k, 疯狂裁员的背后, 技术人才需求依然旺盛, 快来投简历
老程序员肺腑忠告:千万别一辈子靠技术生存!
算法 | 动画+解析,轻松理解「Trie树」相关推荐
- 看动画轻松理解「Trie树」
作者 | 程序员小吴 责编 | 胡巍巍 扎心!"我学了半年 Python,还是找不到工作" https://edu.csdn.net/topic/python115?utm_sou ...
- 【数据结构与算法】【字符串匹配】Trie树
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 一. 什么是"Trie树"? 1. 他是一种树形结构,是一种专门 ...
- 数据结构与算法专题——第十二题 Trie树
今天来聊一聊Trie树,Trie树的名字有很多,比如字典树,前缀树等等. 一:概念 下面有and,as,at,cn,com这几个关键词,构建成 trie 树如下. 从上面图中,应该可以或多或少的发现一 ...
- 机器人大牛 Daniela Rus 领衔!MIT 新算法实现软体机器人「本体感知」
本文转自雷锋网,如需转载请至雷锋网官网申请授权. 说起软体机器人,或许很多人都不觉得陌生了. 软体机器人的发展离不开包括材料学.机器人学.生物力学.传感与控制在内的多学科进步,近年来相关学科迅速发展, ...
- php 字典树实现,数据结构之「字典树」
字典树 字典树,又称 前缀树 或 trie树,是一种有序树,用于保存关联数组,其中的键通常是字符串.与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定.一个节点的所有子孙都有相同的前 ...
- 看动画轻松理解「链表」实现「LRU缓存淘汰算法」
作者 | 程序员小吴,哈工大学渣,目前正在学算法,开源项目 「 LeetCodeAnimation 」5500star,GitHub Trending 榜连续一月第一. 本文为 AI科技大本营投稿文章 ...
- 看动画轻松理解「链表」实现「 LRU 缓存淘汰算法」
作者 | 吴至波 责编 | 胡巍巍 快速挑战Python全栈工程师: https://edu.csdn.net/topic/python115?utm_source=csdn_bw 前几节学习了「链表 ...
- java分治法求数列的最大子段和_Java十大经典排序算法动画解析和 代码实现
排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序. 内部排序是数据记录在内存中进行排序. 而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排 ...
- 下划线间隔数字 排序_面试必备:经典算法动画解析之希尔排序
哈喽,我是程序员大鹏. 前面我们介绍了冒泡排序.选择排序和插入排序,今天我们来看一下进阶的排序. 1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版.它与插入排序的不同之 ...
最新文章
- UART, SPI, IIC的详解及三者的区别和联系
- zynq 文件系统中加载PL fpga.bit笔记
- 原生ajax如何执行,原生ajax调用数据实例讲解
- PADS中Layer25层的作用(Z)
- github 和git_学习编码时如何学习Git和GitHub
- Android下结束进程的方法
- 解决IE下不支持placeholder属性可以根据自己的需要去扩展
- 峰Spring4学习(5)bean之间的关系和bean的作用范围
- Makedown文件保存问题
- matlab 中for 的控制表达式用数组,循环指数可以为向量
- wps多人协作后怎么保存_办公必备效率神器!在线协作文档工具,值得一看
- 【帧率倍频】基于FPGA的视频帧率倍频系统verilog开发实现
- 2018医学考博英语阅读理解解题技巧
- Uni-app开发App和插件以后如何开通广告盈利:uni-AD
- HPUX 11iV3 LVM新变化
- Eclipse使用入门
- 9个你需要知道的PHP函数和功能
- CentOS7.5 重装 SSH 与 禁止网卡休眠
- django之十一--开发一个简单的醉得意菜单和人均支付金额查询页面
- 【转载】通过 FUNCTION NET_DUE_DATE_GET 得到MIRO付款日期(DUE ON DATE)
热门文章
- 面向中小企业的视频云服务 视频托管
- MIDP2.0引入了Push注册机制
- mysql filter_MySQL 过滤复制+复制映射 配置方法
- 201-3-19李宏毅机器学习视频笔记七(游戏解释Gradient Descent)
- 上帝和面向对象的七天
- “error : unknown filesystem”的解决办法
- linxu passwd 给linux用户设置密码 命令
- (转) SpringBoot非官方教程 | 第二篇:Spring Boot配置文件详解
- Android Studio导入Eclipse项目的两种方法
- jvm七种垃圾收集器