trie 和 radix trie 是相同的数据结构吗?
如果它们不同,那么 radix trie(也称为 Patricia trie)的含义是什么?
trie 和 radix trie 是相同的数据结构吗?
如果它们不同,那么 radix trie(也称为 Patricia trie)的含义是什么?
基数树是字典树的一种压缩形式。在字典树中,每个边上只写一个字母,而在PATRICIA树(或基数树)中,您存储整个单词。
现在,假设您有单词hello
、hat
和have
。要将它们存储在字典树中,它会像这样:
e - l - l - o
/
h - a - t
\
v - e
你需要九个节点。我已经将字母放在节点上,但实际上它们标记的是边缘。
在基数树中,您将拥有:
*
/
(ello)
/
* - h - * -(a) - * - (t) - *
\
(ve)
\
*
你只需要五个节点。在上面的图片中,节点是星号。
因此,总体而言,基数树占用的内存较少,但实现起来更困难。否则,两者的用例几乎相同。
hatchet
,它将如何适应上层结构? - mohsenmadi'\0'
字节,这意味着它实际上是 h - a - t - \0
,在 \0
之前会有另一个分支 c - h - e - t - \0
。 - autisticroot['s']['m']['i']['l']['e']['\0'] = smile_item;
root['s']['m']['i']['l']['e']['d']['\0'] = smiled_item;
root['s']['m']['i']['l']['e']['s']['\0'] = smiles_item;
root['s']['m']['i']['l']['i']['n']['g']['\0'] = smiling_item;
smile_item
,需要访问七个节点。八个节点访问对应于 smiled_item
和 smiles_item
,九个节点对应于 smiling_item
。对于这四个项,总共有十四个节点。然而,它们都有相同的前四个字节(对应于前四个节点)。通过将这四个字节压缩以创建一个与 ['s']['m']['i']['l']
相对应的 root
,优化了四个节点访问。这意味着更少的内存和更少的节点访问,这是一个非常好的迹象。可以递归地应用这种优化,以减少访问不必要的后缀字节的需要。最终,您会到达一个只比较搜索键和由 trie 索引的键在位置上的差异的点。这就是基数 trie。root = smil_dummy;
root['e'] = smile_item;
root['e']['d'] = smiled_item;
root['e']['s'] = smiles_item;
root['i'] = smiling_item;
root.position
为4,我们访问root["smiles"[4]]
,这恰好是root['e']
。我们将其存储在名为current
的变量中。current.position
为5,这是"smiled"
和"smiles"
之间差异的位置,因此下一个访问将是root["smiles"[5]]
。这将带我们到smiles_item
,并结束了我们的字符串。我们的搜索已终止,并且已经检索到该项,只需进行三次节点访问,而不是八次。
PATRICIA trie是基数树的一种变体,其中应仅使用n
个节点来包含n
个项目。在我们上面简单演示的基数树伪代码中,总共有五个节点:root
(它是一个零元节点;它不包含任何实际值),root['e']
,root['e']['d']
,root['e']['s']
和root['i']
。在PATRICIA trie中,应该只有四个。让我们通过以二进制形式查看这些前缀的不同之处来了解它们的区别,因为PATRICIA是一种二进制算法。
smile: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0000 0000 0000 0000
smiled: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0110 0100 0000 0000
smiles: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0111 0011 0000 0000
smiling: 0111 0011 0110 1101 0110 1001 0110 1100 0110 1001 0110 1110 0110 0111 ...
Trie(字典树):
可以使用一种搜索方案,其中不是将整个搜索关键字与所有现有关键字进行比较(例如哈希方案),而是可以逐个字符地比较搜索关键字。按照这个想法,我们可以构建一个结构(如下所示),其中有三个现有的关键字 -“dad”、“dab”和“cab”。
[root]
...// | \\...
| \
c d
| \
[*] [*]
...//|\. ./|\\... Fig-I
a a
/ /
[*] [*]
...//|\.. ../|\\...
/ / \
B b d
/ / \
[] [] []
(cab) (dab) (dad)
这实际上是一棵M叉树,内部节点用 [ * ] 表示,叶子节点用 [ ] 表示。这种结构被称为trie。每个节点的分支决策可以保持等于字母表中唯一符号的数目,例如 R。对于小写英文字母 a-z,R=26;对于扩展ASCII字母表,R=256;对于二进制数字/字符串,R=2。
紧凑trie:
通常,trie 中的一个节点使用大小为 R 的数组,因此当每个节点具有较少的边缘时会造成内存浪费。为了解决内存问题,提出了各种建议。基于这些变化,trie 也被命名为“compact trie”和“compressed trie”。尽管一致的命名方法很少见,但最常见的紧凑 trie 版本是在节点只有单个边缘时将所有边缘分组。使用此概念,以上(图-I)包含关键字“dad”、“dab”和“cab”的trie 可以采用以下形式。
[root]
...// | \\...
| \
cab da
| \
[ ] [*] Fig-II
./|\\...
| \
b d
| \
[] []
注意,每个“c”、“a”和“b”都是其相应父节点的唯一边缘,因此它们被合并成一个单一的边缘“cab”。同样,‘d’和‘a’被合并为标记为“da”的单一边缘。
基数 Trie:
在数学中,“基数”意味着数字系统的基础,它实际上表示表示该系统中任何数字所需的唯一符号的数量。例如,十进制系统是基数十,二进制系统是基数二。使用类似的概念,当我们有兴趣通过底层表示系统的唯一符号数来表征数据结构或算法时,我们会将该概念标记为“基数”。例如,用于某些排序算法的“基数排序”。在同样的逻辑线上,所有依赖于基数的字母表的特征(如深度、内存需求、搜索未命中/命中运行时间等)的trie的变体,我们可以称之为基数“trie”。例如,当使用字母a-z时,未压缩的trie以及压缩的trie,我们可以称之为基数26 trie。只使用两个符号(传统上为“0”和“1”)的任何trie都可以称为基数2 trie。然而,一些文献仅限制使用术语“基数Trie”仅用于压缩的trie。
PATRICIA树/trie 的前奏:
有趣的是,甚至字符串键也可以使用二进制字母表表示。如果我们假设ASCII编码,则将密钥“dad”写成二进制形式可以通过按顺序写入每个字符的二进制表示来完成,例如“01100100 01100001 01100100”即写二进制形式的‘d’、‘a’和‘d’。使用这个概念,可以形成一个基数为二的trie。下面我们使用一个简化的假设来描述这个概念,即字母’a’、’b’、’c’和’d’来自一个较小的字母表而不是ASCII。
注意Fig-III:
如上所述,为了使描绘容易,让我们假设只有4个字母{a,b,c,d}和它们对应的二进制表示分别为“00”,“01”,“10”和“11”。有了这个,我们的字符串键“dad”、“dab”和“cab”变成了“110011”,“110001”和“100001”。对应的trie如图III所示(位从左到右读取,就像字符串从左到右读取一样)。 [root]
\1
\
[*]
0/ \1
/ \
[*] [*]
0/ /
/ /0
[*] [*]
0/ /
/ /0
[*] [*]
0/ 0/ \1 Fig-III
/ / \
[*] [*] [*]
\1 \1 \1
\ \ \
[] [] []
(cab) (dab) (dad)
PATRICIA Trie/Tree:
PATRICIA 树(称为“可扩展字母缩写树”)是一种数据结构,它是由 Donald R. Morrison 在 1968 年提出的。通过去除单向边(即仅有一个子节点的分支),他既消除了内部节点和叶子节点的区别,也极大地减少了节点数量。相比之下,上图中的二进制 trie 经过单边压缩后,其节点数量仍然大于包含键数的 3 个节点。PATRICIA 树的每个节点包含一个值,用于指示需要跳过多少位以进行分支决策。不同于其他压缩逻辑,它不存储键本身,因此无法用于列出与给定前缀匹配的所有键,但可用于查找某个键是否存在于 trie 中。值得注意的是,“Patricia Tree” 或 “Patricia Trie” 这个术语已被广泛应用于许多类似但不完全相同的概念,例如表示紧凑的 trie(NIST),或者表示基数为二的基数 trie(如维基百科中微妙的表述)等。
Trie that may not be a Radix Trie:
三叉搜索树(Ternary Search Trie,又称Ternary Search Tree)是一种数据结构(由 J. Bentley 和 R. Sedgewick 提出),看起来非常类似于具有三向分支的 trie 树。对于此类树,每个节点都有一个特征字母'x',以便通过键的字符是小于、等于还是大于'x'来进行分支决策。由于它具有固定的3向分支特性,因此在R(基数)很大时,如Unicode字母表中,它提供了一种内存占用更少的替代trie的方法。与(R路)trie不同,TST的特征没有受到R的影响。例如,TST的搜索未命中时间复杂度为ln(N),而R-way Trie的为logR(N)。此外,TST的内存需求也不是R的函数。因此,在称呼TST为基数trie时要小心谨慎。我个人认为我们不应该将其称为基数trie,因为我所知道的其特征都不受其底层字母表的基数R的影响。
uintptr_t
作为你的整数,因为这种类型似乎通常被期望存在(尽管不是必需的)。 - autistic 9 * 4 + 8 * 9 = 108 bytes.
6 * 4 + 5 * 9 + 8 * 1 = 77 bytes.
radix-trie
而不是radix-tree
有点烦人吗?而且已经有相当多的问题使用了这个标签。 - errantlinguistradix = 2
的基数树,这意味着您需要每次查找输入字符串的log2(radix)=1
位来遍历该树。 - Amelio Vazquez-Reina