文章目录
- 前言
- 参考目录
- 学习笔记
- 0:前置介绍
- 0:前置介绍
- 1:单词查找树 R-way tries
- 1.1:介绍
- 1.2:查找操作
- 1.2.1:成功命中
- 1.2.2:未命中
- 1.3:插入操作
- 1.4:Java 实现
- 1.5:性能
- 1.6:删除操作
- 1.7:小结
- 2:三向单词查找树 ternary search tries
- 2.1:介绍
- 2.2:查找操作
- 2.2.1:成功命中
- 2.2.2:未命中
- 2.3:Java 实现
- 2.4:字符串 ST 实现开销小结
- 2.5:混合三向单词查找树 TST with R^2^ branching at root
- 2.6:字符串 ST 实现开销小结
- 3:基于字符的操作 character-based operations
- 3.1:字符串符号表 API
- 3.2:热身:有序迭代
- 3.3:前缀匹配
- 3.4:最长前缀
- 3.5:压缩前缀树 | 帕特里夏前缀树 Patricia trie
- 3.6:后缀树
- 3.7:小结
前言
本篇主要内容包括:单词查找树、三向单词查找树、基于字符的操作。
参考目录
- B站 普林斯顿大学《Algorithms》视频课
(请自行搜索。主要以该视频课顺序来进行笔记整理,课程讲述的教授本人是该书原版作者之一 Robert Sedgewick。) - 微信读书《算法(第4版)》
(本文主要内容来自《5.2 单词查找树》) - 官方网站
(有书本配套的内容以及代码)
学习笔记
注1:下面引用内容如无注明出处,均是书中摘录。
注2:所有 demo 演示均为视频 PPT demo 截图。
注3:如果 PPT 截图中没有翻译,会在下面进行汉化翻译,因为内容比较多,本文不再一一说明。
0:前置介绍
符号表 ST 实现性能:
0:前置介绍
符号表 ST 实现性能:
如果我们能避免检查整个键(例如,在字符串排序时),并利用数组访问进行 R 路决策(而不是二进制决策),那么我们就能做得更好。
字符串 ST 基本 API:
字符串 ST 实现开销:
1:单词查找树 R-way tries
对应书本章节《5.2.1 单词查找树》。
它的英文单词 trie 来自于 E.Fredkin 在 1960 年玩的一个文字游戏,因为这个数据结构的作用是取出(retrieval)数据,但发音为 try是为了避免与 tree 相混淆。
1.1:介绍
单词查找树:(Tries,词源 “retrieval”,发音为 “try”)
- 在节点中存储字符(而非整个键)。
- 每个节点有 R 个子节点,分别对应可能的每个字符。(目前,我们不绘制空链接)
书本对应的示意图:
1.2:查找操作
单词查找树中的每个结点都包含了下一个可能出现的所有字符的链接。从根结点开始,首先经过的是键的首字母所对应的链接;在下一个结点中沿着第二个字符所对应的链接继续前进;在第二个结点中沿着第三个字符所对应的链接向前,如此这般直到到达键的最后一个字母所指向的结点或是遇到了一条空链接。
1.2.1:成功命中
遍历键中每个字符所对应的链接。
- 搜索命中:当搜索结束时到达的节点具有非空值。
搜索 “shells”:
初始状态:
从根节点开始进行搜索,最终能够成功命中:
返回与最后一个关键字符关联的值(返回3)。
搜索 “she”:
同样从根节点开始进行搜索,最终能够成功命中:
搜索可能在中间节点处终止(返回0)。
1.2.2:未命中
遍历键中每个字符所对应的链接。
- 搜索未命中:到达空链接,或搜索结束位置的节点具有空值。
搜索 “shell”:
没有值与最后一个键字符关联(返回 null)。
搜索 “shelter”:
没有链接(无法到达)t(返回 null)。
1.3:插入操作
遍历键中每个字符所对应的链接。
- 遇到空链接时:创建新节点。
- 遇到最后一个键字符时:在该节点设置值。
对应书本中的介绍:
插入 “shore”,值为 7:
初始状态:
从根节点开始搜索,创建新节点,把最后一个字符的值设置为 7:
树的完整构建轨迹:
1.4:Java 实现
edu.princeton.cs.algs4.TrieST
edu.princeton.cs.algs4.TrieST#put
edu.princeton.cs.algs4.TrieST#get
1.5:性能
搜索命中: 需要检查所有L个字符是否相等。
搜索未命中:
- 可能第一个字符就不匹配。
- 常见情况:只需检查少数几个字符(低于线性时间复杂度)。
空间占用: 每个叶子节点有 R 个空链接。
(但如果许多短字符串共享相同前缀,则有可能实现亚线性空间占用)
最重要: 搜索命中速度快,即使搜索未命中也更快,但会浪费空间。
1.6:删除操作
从一棵单词查找树中删去一个键值对的第一步是,找到键所对应的结点并将它的值设为空(null)。如果该结点含有一个非空的链接指向某个子结点,那么就不需要再进行其他操作了。如果它的所有链接均为空,那就需要从数据结构中删去这个结点。如果删去它使得它的父结点的所有链接也均为空,就需要继续删除它的父结点,依此类推。
删除 “shells”:
要删除一个键值对:
- 找到对应于键的节点并将值设为空(null)。
- 如果该节点的值为空,并且所有链接也都为空,则移除该节点(并递归处理子节点)。
书本对应的轨迹图:
1.7:小结
R 路单词查找树(R-way trie):
- 当R较小时的首选方法。
- 对于较大的R,内存消耗过多。
挑战: 使用更少的内存,例如,对于 Unicode 设计一个 65536 路单词查找树!
2:三向单词查找树 ternary search tries
对应书本章节《5.2.3 三向单词查找树》。
2.1:介绍
- 在节点中存储字符和值(而非完整的键)。
- 每个节点拥有三个子节点:较小值(左)、相等值(中)和较大值(右)。
书本对应的示意图:
2.2:查找操作
遍历键中每个字符所对应的链接。
- 若字符较小,则走左侧链接;若字符较大,则走右侧链接。
- 若字符相等,则取中间链接并移动到下一个键字符。
搜索命中: 搜索结束时所在的节点具有非空值。
搜索未命中: 到达空链接,或搜索结束位置的节点具有空值。
书本相关说明:
在查找时,我们首先比较键的首字母和根结点的字母。如果键的首字母较小,就选择左链接;如果较大,就选择右链接;如果相等,则选择中链接。然后,递归地使用相同的算法。如果遇到了一个空链接或者当键结束时结点的值为空,那么查找未命中;如果键结束时结点的值非空则查找命中。
2.2.1:成功命中
搜索 “sea”:
返回与最后一个关键字符关联的值(返回 6)。
2.2.2:未命中
搜素 “shelter”:
没有链接(无法到达)t(返回 null)。
2.3:Java 实现
edu.princeton.cs.algs4.TST
edu.princeton.cs.algs4.TST#put
edu.princeton.cs.algs4.TST#get
2.4:字符串 ST 实现开销小结
注解: 通过旋转操作可以构建平衡 TST,从而在最坏情况下达到 L + logN
的时间复杂度保证。
最重要: TST 在处理字符串键时速度与哈希表相当,同时具备空间效率优势。
2.5:混合三向单词查找树 TST with R2 branching at root
混合 R 路单词查找树和 TST 的结构:
- 在根部进行 R 平方分支。
- R 个 2 路根节点各自指向一个 TST。
2.6:字符串 ST 实现开销小结
最重要: 对于我们的基准客户端,这种方案比哈希更快。
哈希算法:
- 需要检查整个键。
- 搜索命中和未命中的成本大致相同。
- 性能取决于哈希函数的质量。
- 不支持有序符号表操作。
TST:
- 只适用于字符串(或数字)类型的键。
- 只需检查足够多的关键字符即可。
- 搜索未命中可能仅涉及几个字符。
- 支持有序符号表操作(以及额外功能)。
最重要:
- TST 在搜索(特别是搜索未命中时)上比哈希更快。
- 相比红黑 BST,TST 更为灵活。
3:基于字符的操作 character-based operations
3.1:字符串符号表 API
**基于字符的操作:**字符串符号表 API 支持几种有用的基于字符的操作。
前缀匹配(Prefix match): 对于前缀为 “sh” 的键,可以找到匹配项如 “she”、“shells” 和 “shore”。
通配符匹配(Wildcard match): 符合模式 “.he” 的键,即能找到包含子串 “he” 的键,例如 “she” 和 “the”。
最长前缀(Longest prefix): 能够找到作为 “shellsort” 最长前缀的键,即 “shells”。
字符串 ST 基本 API:
3.2:热身:有序迭代
按排序顺序遍历所有键:
- 对 trie 进行中序遍历;将遇到的键添加到队列中。
- 在从根节点到当前节点的路径上维护字符序列。
3.3:前缀匹配
3.4:最长前缀
在符号表中查找最长的且是查询字符串前缀的键。
- 搜索查询字符串。
- 记录下遇到的最长键。
可能出现的情况:
3.5:压缩前缀树 | 帕特里夏前缀树 Patricia trie
Patricia trie: 帕特里夏前缀树,其英文全称为 “Practical Algorithm to Retrieve Information Coded in Alphanumeric”,意指一种针对字母数字编码信息进行高效检索的实用算法。
- 删除单向分支:在帕特里夏树中消除只有一个后代的节点,使得每个节点都代表一组连续的字符。
- 每个节点表示一串字符:在该数据结构中,每个内部节点关联了一段字符序列。
- 实现细节:这部分内容超出了本次课程的范围,需要进一步学习和实践。
应用:
- 数据库搜索:在数据库中快速查找具有相同前缀的记录。
- P2P 网络搜索:在点对点网络中高效地查找匹配前缀的信息。
- IP 路由表查找:在网络路由中寻找最长前缀匹配,确定数据包的最佳传输路径。
- 压缩四叉树在 N 体模拟中的应用:可用于高效的多体系统空间划分和搜索。
- 高效存储和查询 XML 文档:用于优化 XML 文档的存储方式,提升查询效率。
此外: Patricia trie 也被称为 “crit-bit 树” 或 “基数树”(radix tree)。
3.6:后缀树
后缀树:
- 字符串所有后缀的帕特里夏前缀树表示形式。
- 线性时间构建算法:这一内容超出了本课程的范围。
应用:
- 线性时间复杂度算法的应用:最长重复子串、最长公共子串、最长回文子串、子串搜索、串联重复序列等。
- 计算生物学数据库(如BLAST、FASTA)中的应用。
3.7:小结
算法设计与分析中的成功案例:
红黑 BST(二叉搜索树):
- 性能保证:对数级别的键比较次数(O(log N))。
- 支持有序符号表API。
哈希表:
- 性能保证:常量级别的探测次数(O(1)理想情况下)。
- 需要为键类型提供良好的哈希函数。
Trie(单词查找树): R 路、TST
- 性能保证:访问对数级别的字符数(O(log N)。
- 支持基于字符的操作。
总结: 实际上,只需要查看大约50-100位的数据(!!!),理论上可以访问任何数据。
(完)