【问题标题】:Dictionary data structure + fast complexity methods字典数据结构+快速复杂度方法
【发布时间】:2015-05-26 12:37:11
【问题描述】:

我正在尝试从头开始构建一个能够容纳大量(单词/字符)字典的数据结构。

“单词”可以由任意数量的字符组成。

字典需要标准方法,例如搜索、插入、删除。

我需要方法的时间复杂度O(log(n))更好,所以在O(log(n))O(1)之间,例如log(log(n))

其中 n = 字典大小(元素数量)

我研究了各种树结构,例如具有 log(n) 方法(不够快)的 b-tree 以及似乎最适合字典的 trie,但由于单词可以任意大,它的复杂性似乎不会比log(n) 快。

如果可以的话,请提供任何解释

【问题讨论】:

  • 这里的n 是什么意思?
  • 字典大小通常。
  • trie 的内存需求很大,但访问时间当然比O(log n) 快。如果我没记错的话,访问时间取决于单词的长度,而不是结构中单词的数量。这还取决于您使用的 trie 的具体实现。
  • 不妨把它变成一个答案,他没有说任何关于内存约束的事情。
  • 哈希表不适合您的要求有什么原因吗?

标签: java algorithm dictionary data-structures big-o


【解决方案1】:

trie 需要大量内存,但访问时间通常O(log n) 快。

如果我没记错的话,访问时间取决于单词的长度,而不是结构中单词的数量。

效率和内存消耗还取决于您选择使用的 trie 的具体实现。那里有一些非常有效的实现。

有关 Tries 的更多信息,请参阅:

http://en.wikipedia.org/wiki/Trie

http://algs4.cs.princeton.edu/52trie/

http://algs4.cs.princeton.edu/52trie/TrieST.java.html

https://www.topcoder.com/community/data-science/data-science-tutorials/using-tries/

【讨论】:

  • 也许您可以提供一些提到的实施策​​略的链接,甚至在这里提供它们(示意图)? :)
  • "访问时间当然比O(log n)快":不,访问时间是O(m)对于长度为m的单词,你可以很好地拥有m>log(n)。跨度>
  • @YvesDaoust 是的,当然,但我认为通常会有相反的不平等。所以是的,这就是我的意思。当然,它在某种程度上也取决于对数的底数。任何人都可以做数学给定,例如字典中的 100,000 个单词并给出例如自然对数。
  • OP 明确声明了长字符串。对于一百万个条目,Log(n) 只是 20。在我的理解中,一个 20 个字符的字符串根本不长。
  • @YvesDaoust 好吧,我认为“任意大 长字符串”......无论如何,你为什么不发布答案,但继续寻找我输入的每个单词?我们在同一页上,任何人都可以做基本的数学,好吗?如果你有更好的答案 - 为什么不写呢。
【解决方案2】:

如果您的问题是如何实现尽可能少的字符串比较,那么哈希表可能是一个很好的答案,因为它需要接近O(1)字符串比较。请注意,散列键值所花费的时间与字符串长度成正比,字符串比较的时间也是如此。

但这并不是什么新鲜事。我们可以为长字符串做得更好吗?更准确地说,我们将假设字符串长度以M 为界。我们还将假设每个字符串的长度都是已知的(对于长字符串,这可能会有所不同)。

首先请注意,搜索时间受字符串长度的限制,在最坏的情况下为Ω(M):比较两个字符串可能需要比较所有字符,因为字符串只能在最后一个字符比较中有所不同。另一方面,在最好的情况下,比较可以立即结束,要么是因为长度不同,要么是因为字符串在比较的第一个字符中不同。

现在您可以进行如下推理:考虑字典中的整个字符串集,并找出它们不同的第一个字符的位置。根据此字符的值,您将分解为多个子集。你可以递归地继续这种分解,直到你得到单例。

例如,

able  
about  
above  
accept  
accident  
accompany  

组织为

*bl*
*bou*
*bov*
*c*e**
*c*i****
*c*o*****

其中星号代表刚刚忽略的字符,其余字符用于区分。

如您所见,在这个特定示例中,两个或三个字符的比较足以识别字典中的任何单词。

这种表示可以被描述为一个有限状态自动机,这样在每个状态中,您都知道接下来要检查哪个字符以及可能的结果是什么,从而导致下一个状态。它有一个K-ary 树结构(其中K 是字母表的大小)。

对于一个有效的实现,每个状态都可以由决策字符的位置和一个 array 链接到下一个状态来表示。实际上,这是一个 trie 结构,带有路径压缩。 (正如@peter.petrov 所说,trie 结构有很多变体。)

我们如何使用它?有两种情况:

1) 已知搜索字符串在字典中:那么保证对树的简单遍历就能找到它。它会在与树O(D) 中相应叶的深度相等的字符比较次数后执行此操作,其中D 是此深度。这可以节省大量资金。

2) 搜索字符串可能不在字典中:在树的遍历过程中,您可以观察到早期拒绝;否则,最后你会找到一个潜在的匹配。然后你不能避免进行详尽的比较,最好的情况是O(1),最坏的情况是O(M)。 (对于随机字符串,平均 O(M),但对于实际分布可能更好。)但是您将与单个字符串进行比较,永远不会更多。

除了该设备之外,如果您的密钥长度分布是稀疏的,维护密钥长度的哈希表可能会很有用,这样可以立即拒绝搜索字符串。

作为最后的评论,请注意此解决方案的成本不是N 的直接函数,并且很可能M 中的时间次线性可以通过利用字符串的特定分布的适当启发式来实现.

【讨论】:

  • 在 trie 或哈希表中的插入和删除是完全可能的。无论如何,这个答案并没有解决在插入和删除的情况下动态存储字符串的问题,这是一个更难的问题。
猜你喜欢
  • 2017-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
  • 2020-10-01
  • 2011-08-17
相关资源
最近更新 更多