18

我不知道这是否是询问算法的地方。但是让我们看看我是否得到任何答案...... :)

如果有什么不清楚的地方,我很乐意澄清事情。

我刚刚在 python 中实现了一个Trie 。然而,有一点似乎比它应该的更复杂(作为一个喜欢简单的人)。也许有人遇到过类似的问题?

我的目标是通过将子树的最大公共前缀存储在其根中来最小化节点数量。例如,如果我们有单词stackoverflowstackbasestackbased,那么树看起来像这样:

              [s]tack
[o]verflow ______/ \_______ [b]ase
                                  \___ [d]

请注意,仍然可以认为边缘具有一个字符(子节点的第一个字符)。

Find -query 很容易实现。 插入并不难,但比我想要的要复杂一些.. :(

我的想法是一个接一个地插入键(从一个空的 trie 开始),首先搜索要插入的键 k(Find (k)),然后在本地重新排列/拆分节点查找过程停止。结果有4种情况:(设k是我们要插入的键,k'是节点的键,搜索结束的地方)

  1. k 与 k' 相同
  2. k 是 k' 的“正确”前缀
  3. k' 是 k 的“正确”前缀
  4. k 和 k' 共享一些共同的前缀,但没有出现 (1)、(2) 或 (3) 的情况。

似乎每个案例都是独一无二的,因此意味着对 Trie 的不同修改。但是:真的那么复杂吗?我错过了什么吗?有更好的方法吗?

谢谢 :)

4

5 回答 5

19

乍一看,听起来您已经实现了Patricia Trie。这种方法在一些文献中也称为路径压缩。应该有该论文的副本不在 ACM 付费墙后面,其中将包括插入算法。

您可能还想了解另一种压缩方法:级别压缩。路径压缩背后的想法是用具有“跳过”计数的单个超级节点替换单个子节点的字符串。级别压缩背后的想法是用具有“度”计数的超级节点替换完整或几乎完整的子树,该“度”计数表示节点解码的密钥位数。还有第三种方法称为宽度压缩,但我担心我的记忆让我失望,我无法通过快速谷歌搜索找到它的描述。

级别压缩可以大大缩短平均路径,但插入和删除算法变得相当复杂,因为它们需要像管理动态数组一样管理 trie 节点。对于正确的数据集,级别压缩树可以很快。据我所知,它们是存储 IP 路由表的第二快的方法,最快的是某种哈希树。

于 2009-06-07T02:09:25.913 回答
2

我认为您的方法没有任何问题。如果您正在寻找一个尖峰解决方案,也许在案例 4 中采取的行动对于前三种情况实际上是可行的,IE 找到公共前缀kk'在考虑到这一点的情况下重建节点。如果键是彼此的前缀,则生成的 trie 仍然是正确的,只是实现比实际需要做的工作多一点。但是话又说回来,没有任何代码可以查看,很难说这是否适用于您的情况。

于 2009-06-07T01:21:00.317 回答
2

有点切线,但如果你非常担心 Trie 中的节点数量,你也可以考虑加入你的单词后缀。我会看看 DAWG(有向无环词图)的想法:http ://en.wikipedia.org/wiki/Directed_acyclic_word_graph

这些的缺点是它们不是很有活力,创建它们可能很困难。但是,如果您的字典是静态的,它们可以非常紧凑。

于 2009-06-07T05:33:28.310 回答
2

我对您的实施有疑问。您决定拆分字符串以制作前缀树的粒度级别是多少。您可以将堆栈拆分为 s,t,a,c,k 或 st,ta,ac,ck 以及它的许多其他 ngram。大多数前缀树实现都考虑了语言的字母表,基于这个字母表,您可以进行拆分。

如果您正在为 python 构建前缀树实现,那么您的字母表将是 def、:、if、else... 等

选择正确的字母表对于构建有效的前缀树有很大的不同。至于您的答案,您可以在 CPAN 上查找 PERL 包,这些包使用 trie 进行最长公共子串计算。你可能会有一些运气,因为它们的大部分实现都非常健壮。

于 2009-06-07T05:46:21.657 回答
1

查看:http ://www.dalkescientific.com/Python/PyJudy.html 上的 Judy-arrays 和 python 接口

于 2009-06-13T08:05:25.410 回答