将二进制堆的大小限制为前N个元素

时间:2011-12-02 20:32:19

标签: algorithm heap

我一直在研究二进制堆,它们显然是优先级队列的良好数据结构。假设我的数据流有数百(N)个记录,并且我定期对排名前1000(k

(如果我对每次修剪(和插入)的O(k)时间感到满意,我只维护一个有序的k元素列表,而不是堆。)

一个想法是有两个平行的堆,一个保持分钟,另一个保持最大值,两者都只保留前1000个元素。但这有点难看。

只是为了澄清,这些是我的约束:

  • 插入:理想情况下小于~1000个操作(因此排除原始列表)
  • 存储:有限,需要大致以插入速率修剪不受欢迎的项目(某些常量开销是正常的)
  • 查询前1000名:前1000个项目不必完美排序,堆排序很好

3 个答案:

答案 0 :(得分:3)

您可以使用二进制堆轻松完成此操作。

假设您有一些未知大小的项目流,并且您想要找到前1000个项目。这是想法。

initialize heap
while (items to be read)
{
    read item
    if (heap.count < 1000 OR item > heap.Peek())
    {
        // Either we haven't added 1,000 items yet,
        // or the new item is larger than the smallest
        // item on the heap.
        heap.Add(item)
        if (heap.count > 1000)
        {
            // trim the heap
            // This makes sure that the heap doesn't
            // grow too large.
            heap.RemoveFirst()
        }
     }
}

heap.Peek()检查但不删除堆上的最低项目。)

完成后,堆将按排名包含前1000个项目。

这不能在O(N)时间内完成。该算法的复杂性为O(N log k),其中k是堆的大小。

顺便说一下,你也不会在O(N)时间内维护一个有序列表。

如果可以将所有1,000,000个项目保留在数组中,则另一个选项是Quickselect。它在O(N)时间内运行,但我发现当kN相比较小时,堆选择技术更快。有关详细信息,请参阅When theory meets practice

如果您无法将所有项目保留在内存中(即您正在处理数据流),那么堆选择技术是您可以做的最好的。您可以使用skip list执行相同的操作,也可以是O(n log k),但跳过列表可能比二进制堆执行得更好。

顺便说一下,O(n log k)是最坏的情况,如果项目按排序顺序呈现给堆,就会发生这种情况。在这种情况下,每个项目都会添加到堆中。如果项目的分发更正常,则大多数项目都不会超过heap.Peek()测试。我的测试表明,通过正态分布,只有大约10%的项目(从1,000,000中选择1,000个)通过了第一次测试。同样,我在上面链接的博客文章中提供了更多信息。

答案 1 :(得分:2)

听起来你需要Min-Max heap

这为你提供了删除最小值和删除最大值的O(log(n))操作,这可以帮助你实现目标。

答案 2 :(得分:1)

堆不适合搜索项目,并且它不会保留元素的顺序以保留前1000个元素,您可以使用O(n)中的balanced binary search tree执行此操作。

编辑:此外,使用min heap获取最大项目的想法已经足够了,我不知道这一点,但我更喜欢BST。