【问题标题】:A data structure for determining how many elements lie within a specific range?用于确定特定范围内有多少元素的数据结构?
【发布时间】:2013-03-22 00:45:58
【问题描述】:

假设我有一组数字。我需要计算给定范围内有多少数字。 例如:对于给定的集合:{3, 4, 7, 10, 15, 30}:

numbers in range (0, 6) = 2
numbers in range (8, 40) = 3
numbers in range (0, 50) = 6

什么样的结构最适合这个目的?最好的意思是具有最快执行所述操作的结构。快速插入和移除也将不胜感激......

【问题讨论】:

    标签: c# algorithm data-structures


    【解决方案1】:

    如果给定的一组数字永远不会改变,一个简单的选择是将数字按升序排序,然后在范围的端点上使用binary search 来确定排序序列中第一个元素的位置包含在范围之内以及不在范围内的第一个元素所在的位置。然后,您可以减去这两个位置的差来计算该范围内有多少元素,或者只是迭代该范围以确定该范围内的所有数字。使用快速排序或堆排序等快速排序算法,排序可以在 O(n log n) 时间内完成,每个查询只需 O(log n) 时间即可完成两次不同的二进制搜索。

    您可以通过多种方式加速此过程。例如,如果您知道这些数字或多或少是均匀分布的,则可以使用interpolation search 而不是二进制搜索来进行查找。这需要预期的时间 ​​O(log log n) 来执行每个查询,这比以前快得多。如果您知道数字都在 [0, N) 范围内,您可以使用更高级的数据结构,例如 van Emde Boas tree,在最坏的情况下将所有操作加速到 O(log log N)。

    另一方面,如果数字集可以增长和缩小,那么您可能需要考虑使用平衡二叉搜索树来存储您的数字。然后,您可以在树上进行有效的搜索(时间为 O(log n))以确定范围内的第一个数字和不在范围内的第一个数字。

    希望这会有所帮助!

    【讨论】:

    • This group 将 vEB 树与红黑树进行比较,发现仅提高了 30%。如果一个普通的旧尝试不比两者都快,我会感到惊讶。所有这些结构的节点都需要增加一个descendant_count 字段。
    • @DavidEisenstat- 这很有趣!我认为这可能与 24 位 log n 和 log log n 都非常小的事实有关。是的,你是对的——你确实需要扩充数据结构。感谢您指出这一点!
    • 您能否详细说明如何使用 O(log n) 对平衡二叉树的搜索来计算给定范围内的元素数?谢谢!
    • 每个节点x存储x的左孩子的后代数量。该字段可以在轮换后在本地更新。在搜索之前,将累加器初始化为零。每当搜索使用节点 y 的右子节点时,将 y 的左后代计数加 1 到累加器。当搜索停止时,累加器加上最后一个元素的左后代计数就是该元素的索引。
    • 感谢@DavidEisenstat。而且我可以看到插入或删除元素如何只需要更改从根到元素的路径上的 O(log n) 计数。
    【解决方案2】:

    这是计算几何中一个经过充分研究的问题,它被称为range searching。虽然你有 1-D 版本。问题是每个操作有多常见,如果插入和删除很少,在这种情况下,您可以将它们制成表格。这将为您提供 O(n^2) 存储和恒定时间查询。

    【讨论】:

      【解决方案3】:

      如果您的数据集不会随时间变化,则模板类型定义的答案很好,但您提到需要快速插入和删除。 [编辑:David Eisenstat 已经解释了如何对平衡二叉树进行两次 O(log n) 搜索并增加每个节点的计数,从而可以有效地计算给定范围内的元素。]

      无论如何,如果需要快速更新,解决问题的理想数据结构是Fenwick tree 或 BIT 树。此数据结构为以下两个操作提供 O(log n) 保证:

      • 查询:计算 0 到任意给定数字之间的元素个数。
      • 更新:将某个给定数量的任何给定数量的副本插入/从多集中删除。

      两个查询调用允许您使用count(j) - count(i) 计算任何给定范围 [i, j) 中的元素数。

      对 Fenwick 树的查询和更新都只涉及对单个数组的简单按位运算和查找,因此使用这种数据结构将在 O(log n) 上产生一个非常有竞争力的常数——我希望它比在更新时维护平衡的二叉树,这需要指针操作和树重新平衡。

      【讨论】:

      • 我发现它的 4-ary 和 8-ary 变体比二进制更快。 YMMV。
      • 该死,你和templatetypedef的答案都很棒,我不知道该接受哪一个......我想我会接受你的,因为我决定使用BIT树。
      【解决方案4】:

      这有什么问题?

          static int Count(IList<int> set, int min, int max)
          { 
              int count = 0;
              foreach (int i in set)
                  if (i < max && i > min)
                      count++;
      
              return count;
          }
      

      【讨论】:

      • O(n)。二叉树会更快。
      猜你喜欢
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      • 2020-11-17
      相关资源
      最近更新 更多