【问题标题】:Implementation of a Data-Structure supporting various operations支持各种操作的数据结构的实现
【发布时间】:2014-04-02 22:07:03
【问题描述】:

我必须实现一个支持以下三个功能的数据结构。数据是一对(a,b)的两个双精度值,数据集中在特定区域。假设“a”的值在 500-600 范围内。

  1. Insert(double a, double b) - 在数据结构中插入数据,一对(double,double)。如果该对的第一个元素已经存在,则将其第二个元素更新为新值。

  2. Delete(double a) - 删除包含第一个元素 = a 的数据。

  3. PrintData(int count) - 打印具有第 count 个最大值的数据的值。根据data.first比较值。

输入文件包含一系列插入、删除和打印数据操作。目前,我已经使用 STL-Map 将数据结构实现为高度平衡的二叉搜索树,但速度还不够快。

还有其他比 Map 更快的实现吗? 我们可以使用缓存来存储最常见的 PrintData 查询。

【问题讨论】:

  • @Inertiatic 根据 #3,优先级队列不会(有效地)支持“count-th maximum value”查询。
  • @Dukeling 我一定误解了这个问题。编辑:你是对的,我知道我在哪里迷路了。 Delete 必须搜索并删除该元素,该元素可以位于数据结构中的任何位置。
  • 数据结构中有哪些元素?除非有成千上万个,否则使用键的有序向量和具有值的并行向量并在其中插入/删除可能要快得多!如果您可以合理地存储元素,例如,使用转换为 int 然后在向量中搜索,您可以有效地将数据结构扩展到更多元素。更新值时,如果计数大于当前值,您还将立即存储当前最大值/计数(无需搜索)。
  • 参数“a”用于排序(比较两个数据值)。但是,我们需要打印第 th 个最大值,而不是最大值。
  • @DietmarKühl 最终数据结构中大约有 6,00,000 个元素。

标签: c++ algorithm data-structures stl


【解决方案1】:

我推荐 2 个二叉搜索树 (BST) - 一个是从 ab 的映射(按 a 排序),另一个应按 b 排序。

第二个需要是自定义 BST - 您需要让每个节点存储子树中节点数的计数,并以它为根 - 这些计数可以在 O(log n) 中更新,并且将允许 O(log n) 查询获得第 k 个最大元素。

进行插入时,您只需先在第一个 BST 中查找 b 的值,然后从第二个 BST 中删除该值,然后更新第一个并将新值插入第二个。

对于删除,您只需在第一个 BST 中查找 b 的值(并删除该对),然后从第二个 BST 中删除该值。

所有提到的操作都需要 O(log n)。

缓存

例如,如果您只想查询前 10 个元素,则可以维护另一个只包含这 10 个元素的 BST(或者甚至只是一个可选排序的数组,因为只有 10 个元素),我们将然后查询而不是上面的第二个 BST。

插入时,如果值大于最小的,也插入到这个结构中,去掉最小的。

移除时,我们需要查找下一个最大值并将其插入到小的 BST 中。虽然这也可以懒惰地完成 - 删除时,只需将其从 BST 中删除 - 不要再次将其填充到 10。查询时,如果这个BST中有足够多的元素,我们就用这个查找,否则我们在大BST中找到所有需要填满这个BST的值,然后查询。

这将导致最佳情况 O(1) 查询(最坏情况 O(log n)),而其他操作仍然是 O(log n)。

虽然增加的复杂性可能不值得 - O(log n) 相当快,即使对于较大的 n。

基于这个想法,我们可以拥有这个小的 BST 以及 BST 映射 ab - 这需要我们检查所有值以在删除后进行查询,因此只有在没有大量删除的情况下才真正有益。

【讨论】:

  • 我们如何在第三个操作中使用缓存,因为 PrintData 函数只接受 1-10 范围内的参数。
  • 如果我们不考虑缓存,为什么第二个(自定义)BST 应该按“b”值排序,因为数据的排序是按“a”值完成的。此外,插入是根据“a”值完成的。即使在缓存之后,我认为它还不够快。我需要减少所有操作占用的 CPU 周期数。
  • @Hellboy:你只需要一个按 a 排序的搜索树。有几种高性能数据结构可用于这项工作,例如 B 树。
  • @Hellboy 如果您正在寻找第 k 个最大的 b 值(或者我误解了那部分?),a 订购的单个 BST 将要求您搜索所有找到它的数据,因为它不是由b 排序的。所以这是 O(n) 与 O(log n) - 差别很大。
  • @Dukeling 我们必须找到第 k 个最大的“a”值,而不是“b”。即使在创建了一个大小为 10 的小 BST 之后,我们也必须在小 BST 的最坏情况下迭代所有元素,因为我们不能在 C++ 中为 Maps 执行迭代器 = 迭代器 + 计数。因此,第二个大小为 10 的 BST 需要什么,因为本次迭代可以使用 reverse_iterator 在主 BST 上完成。
【解决方案2】:

我会推荐indexed skip list。这将使您 O(log n) 插入和删除,以及 O(log n) 访问第 n 个最大值(假设您按降序维护列表)。

跳过列表并不比自平衡二叉树更难实现,并且在某些情况下提供更好的性能。值得考虑。

original skip list paper

【讨论】:

  • 但是跳过列表中最坏情况的插入/删除/搜索时间复杂度是O(n)
  • @Hellboy:是的,最坏的情况是 O(n)。该论文指出,最坏的情况几乎是不可能遇到的。我的经验证明了这一点。我经常使用跳过列表,并且从未遇到过任何接近这种病态行为的事情。我并不是说它不可能发生,只是说它极不可能发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
  • 1970-01-01
相关资源
最近更新 更多