【问题标题】:Efficient datastructure for pooling integers用于池化整数的高效数据结构
【发布时间】:2012-04-18 15:58:41
【问题描述】:

我正在寻找一种数据结构来帮助我管理整数池。这是一个池,我从池中删除整数一会儿,然后将它们放回原处,期望它们会再次被使用。但是,它还有一些其他奇怪的限制,因此常规池不能很好地工作。

硬性要求:

  • 恒定时间访问使用中的最大整数。
  • 整数的稀疏性需要有界(即使只是原则上)。
    我希望整数彼此接近,这样我就可以在范围内使用最少的未使用整数快速迭代它们。

如果它们有助于选择数据结构,则使用它们,否则忽略它们:

  • 池中的整数从 0 开始且连续。
  • 池可以是固定大小的。
  • 池中的整数仅用于短期高流失率。

我有一个可行的解决方案,但感觉不够优雅。
我的(次优)解决方案

固定大小的池。
将所有可用整数放入一个有序集合 (free_set)。
当请求一个新整数时,从 free_set 中检索最小的整数。
将所有正在使用的整数放入另一个排序集 (used_set)。
当请求最大时,从 used_set 中检索最大的。

有一些优化可能有助于我的特定解决方案(优先队列、记忆等)。但我的整个方法似乎很浪费。

我希望有一些深奥的数据结构可以完美地解决我的问题。或者至少是更好的池化算法。

【问题讨论】:

  • 抱歉,堆 + 哈希集(或布隆过滤器,或者什么都没有,如果您不需要跟踪旧整数)有什么问题,当您弹出时只需填写一个新整数出一个旧的(或者当你想用新的整数填充时)?
  • used_set 需要多于一个集合(至少是一个有序集合),否则检索最大整数不会是 O(1) 操作。另外,为什么还要预先填充free_set?当你需要一个新的整数并且空闲池为空时,只需构造一个新的。
  • the sparseness of the integers needs to be bounded (even if only in principal). - 你能详细说明这个要求吗?
  • “基于 0 且连续”是否意味着您总是存储从 0 到 N 的所有整数?如果是这样,是否有任何理由存储除 N 之外的任何内容,因为您可以从 N 生成整个集合的其余部分?如果不是,那是什么意思?
  • @deft_code:所以这实际上是在描述您当前的实现,而不是问题本身。在问题本身中,您只是有一组稀疏的值,以及什么能够在恒定时间内获得最大的(可能是最小的?)?你说你的流失率很高,那么想必你也关心插入和删除速度?当您“搅动”时,您是删除所有当前内容并放入新的,还是插入和删除任意元素,并且需要随时知道最大的?

标签: algorithm optimization data-structures


【解决方案1】:

伪类:

class IntegerPool {

  int size = 0;
  Set<int> free_set = new Set<int>();

  public int Acquire() {
    if(!free_set.IsEmpty()) {
      return free_set.RemoveSmallest();
    } else {
      return size++;
    }
  }

  public void Release(int i) {
    if(i == size - 1) {
      size--;
    } else {
      free_set.Add(i);
    }
  }

  public int GetLargestUsedInteger() {
    return size;
  }
}

编辑

RemoveSmallest 并不是那么有用。 RemoveWhatever 已经足够好了。所以Set&lt;int&gt; 可以替换为LinkedList&lt;int&gt; 作为更快的选择(甚至Stack&lt;int&gt;)。

【讨论】:

  • 当您取 (1,2,3) 并返回 2,3 [按此顺序] 然后要求最大时,此解决方案失败 - 它会返回 2,而答案是 1。我相信可以克服这个问题,但我想这将需要第二组,这将使解决方案衰减为 OP 提出的解决方案。
  • 你可以在减小尺寸时克服它[在 release()] 中:开始从 free_set 中拉出最大的元素,并为你拉出的每个元素减少 size,直到你找到大小之间的“差距”和最高元素。但是,它会增加release() 的延迟,但我怀疑它不会损害摊销时间复杂度。它需要SortedSet,而不是编辑建议的堆栈/列表。
  • 可能是一个向量,其中每个插槽要么是“in_use”,要么是“free”。空闲槽包含前一个和下一个空闲槽的索引。当您获取一个 int 时,它会从空闲列表中获取。如果为空,则将一个槽添加到向量中。当你释放时,所述槽被添加到空闲列表中,然后向量末尾的空闲槽被删除。
【解决方案2】:

为什么不使用平衡二叉搜索树?您可以存储指向 min 元素的指针/迭代器并免费访问它,并在插入/删除后更新它是 O(1) 操作。如果你使用自平衡树,插入/删除是 O(log(n))。详细说明:

insert :只需将新元素与前一个元素进行比较;如果最好让迭代器指向新的最小值。

delete :如果 min 被删除,则在删除之前找到后继者(您可以通过将迭代器向前移动 1 步来完成),然后将该人作为新的 min。

虽然理论上可以使用某种复杂的超级堆数据结构(即斐波那契堆)做得更好,但在实践中,我认为您不会为了节省一点点而想要实现类似的东西对数因子。此外,作为奖励,您可以免费获得快速的顺序遍历——更不用说现在大多数编程语言^都带有开箱即用的自平衡二叉搜索树的快速实现(如红黑树/avl等.).

^ 除了 javascript :P

编辑:想到一个更好的答案。

【讨论】:

  • 您的建议与我目前的解决方案有何不同?
  • 我不太确定你在做什么来维护排序集?此外,如果您不明确跟踪最小值/最大值,则获取项目将花费 O(log(n)) 而不是恒定时间操作。无论如何,你可以简单地论证你不能做得比这更好,因为你可以将你的问题减少到排序。 (只需将一堆元素插入一个数组中,然后调用 take min/delete 足够次 ala heap sort。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多