【问题标题】:Efficient data structure for associating data with filesystem paths?用于将数据与文件系统路径相关联的有效数据结构?
【发布时间】:2012-10-25 04:34:07
【问题描述】:

我需要在内存中保存一些关于可能大量文件和目录(通常多达几十万)的数据。显而易见的做法是使用Dictionary<string, Something>,并以路径为键,但是这样做有两个问题:

  • 许多文件的大部分路径都相同,因此存储每个文件的完整路径可能会浪费内存
  • 我需要能够快速访问有关目录所有后代的数据;使用字典,唯一的方法是测试每个键并检查它是否以指定路径开头,这是非常低效的

这个问题似乎很适合使用前缀树(或trie),路径段作为“字符”。我尝试实现它,通过前缀查找性能还不错(大约比字典快4倍),但它有两个问题:

  • 内存消耗没有减少,可能是因为每个节点的子节点列表的开销
  • 构建时间比使用字典要差得多(填充集合要慢 4 倍左右)

我确定这一定是一个非常常见的问题,所以也许有一些我不知道的众所周知的解决方案?

【问题讨论】:

  • 你看过CloudGraph这样的东西吗?
  • @Tigran,谢谢,但它没有提供任何关于它可以做什么的具体细节......此外,它说“测试版目前计划于 2012 年第一季度发布”,但显然是仍处于测试阶段;我现在需要它,而且我需要它很强大,所以不能选择测试版。
  • @ThomasLevesque:是的,我知道。这只是一个提示(方向)……在我的看来,几乎不可能同时拥有健壮的图形(而不是树)结构、快速构建和选择性能。在其中一些中,您必须“支付”并使用它们才能在 当前 项目中获得最佳结果。顺便说一句,我也很想听听其他人,他们在 .NET 方面有经验。

标签: c# data-structures dictionary path trie


【解决方案1】:

只是一些通用的想法:

首先,Patricia trie 可能是改善尝试的内存消耗的最著名方法 - 它将所有节点都有一个子节点的路径压缩到一个节点中,并沿路径连接字符。还有一个版本,您将数据视为二进制数字序列,其优点是您始终最多有 2 个子节点,并且更易于实现。

其次,内存消耗实际上取决于您如何存储给定节点的子节点 - 您是否维护 256 个节点的数组?这通常是直接查找最有效的方式,但如果您需要遍历所有子项,也会消耗最多的内存并且速度很慢。其他选项是:

  • 存储对数组(letter, child node) - 这可能是最节省内存的方式,因为它只存储您真正关心的对象,并且在遍历所有子对象方面也有很好的性能。但是,您必须检查所有对以进行直接查找 - 这通常离根较远,但在根附近可能会出现问题。

  • 在每个节点内存储某种字典,将字母映射到子节点。这在性能方面是最平衡的——它为所有操作提供了相当不错的速度,并且在一定程度上节省了内存。

另外,如果您预先构建整个集合然后只查询它,则有一种基于Tarjan tables 存储子链接的方法可能会增加构建时间,但会节省内存和稍后的查询时间.

【讨论】:

  • 感谢您的回答。目前我正在使用链表来存储子节点。它不是查找效率最高的,但我不希望在每个级别都有太多节点(典型文件夹结构中的几十个),并且它对于插入非常有效。基数树是一个有趣的优化,但在我的情况下它不适用,因为我需要为路径上的每个级别提供一个不同的节点。 Tarjan 表对我来说似乎有点复杂......
  • 还有一个可以应用的额外优化 - 将此答案与en.wikipedia.org/wiki/Directed_acyclic_word_graph结合起来
  • 是的,DAWG 可能会减少内存使用量,但会大大增加构建时间。这取决于插入的单词是否值得。
  • @icepack,感谢您的链接。就我而言,我更关心前缀冗余而不是后缀冗余,所以它可能不是一个很大的优化。由于它也比尝试实现更复杂,我不确定它是否值得......
【解决方案2】:

前缀树之类的方法怎么样。即如果你想存储

/root/x
/root/a/b
/root/a/c
/root/a/d
/root/a/e
/root/a/c/e
/root/a/c/f
Here is how your tree will look like. 
                       root
                     /    \
                    x   __ a __ 
                       /  / \   \ 
                     b   c    d   e
                        / \
                       e   f

这将节省空间,因为每个目录名称将只存储一次。 搜索和插入也是 O(log(n))

【讨论】:

  • 它实际上是一个前缀树,正如我的问题中提到的,我已经尝试过了。
  • @ThomasLevesque 抱歉,我错过了您已经尝试过前缀树。
猜你喜欢
  • 2012-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-21
  • 2015-11-29
  • 1970-01-01
  • 2019-09-04
相关资源
最近更新 更多