在我的项目中,有一个很大的清单。
此列表上最常见的操作是获取最大的n
元素。
n
在整个生命周期中都是固定的或很少更改的。我应该使用哪种算法才能有效地做到这一点?
这意味着在插入,更新或删除列表中的元素时应该做什么,以及从列表中获取前n个元素时应该做什么。
有一个解决方案(也许不是很好):
quicksort
或其他排序算法对列表进行排序。 由于列表太大,因此此步骤可能太慢。 有更好的解决方案吗?
答案 0 :(得分:3)
因此,您有一个n
个项目的列表,并且想选择最大的k
个。一种方法是使用大小为k
的最小堆。生成的算法为O(n log k)。
首先创建前k
个项目的空白最小堆。然后,对于列表中的每个后续项,如果它大于堆中的最小项,请删除堆中的最小项,然后将其替换为新项。完成后,最大的k
项将在堆上。伪代码如下:
// assume an array a[], with length n.
// k is the number of largest items you want.
heap = new min-heap
// add first k items to the heap
for (i = 0; i < k; ++i)
heap.add(a[i])
for (i = k; i < n; ++i)
if (a[i] > heap.peek())
heap.removeMin()
heap.add(a[i])
// at this point, the largest k items are on the min-heap
当k占n的一小部分时,此技术效果很好。在这种情况下,它只需要很少的内存。该算法的最坏情况下运行时间为O(n log k),但是它高度依赖于列表中项目的顺序。最坏的情况是数组按升序排序。最好的情况是数组按降序排序。在一般情况下,从堆中添加和删除项的数量要少于50%。
另一种算法Quickselect的复杂度为O(n),但是当k占n的一小部分(1或2%)时,它比堆选择方法慢。快速选择还会修改现有列表,而这可能不是您想要的。
有关更多详细信息,请参见我的博客文章https://blog.mischel.com/2011/10/25/when-theory-meets-practice/。
您可以在此处做一些事情,通过维护堆而不是为每个查询重建它来加快平均时间。
那么,结果是您可以以很少的成本维护堆:只要添加新项目,就可以对其进行更新,但前提是该堆必须大于前30个(或最大)项目之一。唯一需要重建的是在删除后要求输入前k个项目。
考虑一下,如果删除的项目大于或等于堆中最小的项目,则只需将堆标记为脏。另外,如果堆被标记为脏堆,那么您可以放弃对插入或删除操作的任何进一步更新,因为无论如何下次您要查询时都必须重新构建堆。
答案 1 :(得分:1)
(平衡的)二进制搜索树是您最好的朋友。插入,删除,始终搜索第k个O(Log N)。
如果数据驻留在外部存储器中,则为B树或类似树。
答案 2 :(得分:0)
如果n <<<< size(list)
,则对主要元素使用哈希表,并使用伴随数据结构存储最大的n个元素。伴随数据结构在插入和删除期间进行更新,并用于查询最大的元素。
如果n为30,则排序数组就足够了。
免责声明:如果经常删除大多数元素,则此方法效果不佳。删除最大元素将需要对整个哈希表进行顺序扫描。
答案 3 :(得分:0)
。 最好的选择是使用std :: set。
每次添加元素时都会对其进行排序。 然后您可以提取std :: set
的最后n个元素