从n个排序数组中总体获得k个最小值的时间复杂度?

时间:2014-01-10 17:16:34

标签: algorithm sorting time-complexity

我有n个数组。这些阵列中的每一个都可以具有无限长度。 (长度可以变化)。所有这些n个数组都已排序。

现在我想从这些n个排序数组中取出前k个最小元素。

例如n = 5且k = 10

2 4 6 7 9 

23 45 67 78 99

1 2 6 9 1000 4567 6567 67876

45 56 67 78 89 102 103 104

91 991 9991 99991 

现在回答应为1 2 4 6 7 9 23 45 56 67

在最坏的情况下,它是O(n * k),即O(n ^ 2),在最好的情况下是O(k)吗?

2 个答案:

答案 0 :(得分:8)

我认为这是O(n + k.log(n))。

首先在每个数组中构建一个最小元素的堆(也存储数组的索引)。构建大小为n的堆是O(n)。然后,重复k次:从堆中取一个元素(即O(log n)),然后插入所用元素来自数组的下一个最小元素(也是O(log n))。总的来说,这是O(n + k.log(n))。

答案 1 :(得分:3)

Anonymous提供的答案在这种情况下是更好的解决方案,因为我们知道各个数组已经排序。

你可以在O(n log k)时间内使用堆来完成,最坏的情况。这将需要O(k)额外空间。

initialize a MAX heap
for each array
    for each item in the array
        if (heap.count < k)
            heap.insert(item)
        else if (item < heap.peek())
        {
            // item is smaller than the largest item on the heap
            // remove the smallest item and replace with this one
            heap.remove_root()
            heap.insert(item)
        }
        else
        {
            break;  // go to next array
            // see remarks below
        }

因为您知道数组最初是排序的,所以您可以包含我展示的最终优化。如果您正在查看的项目不小于堆上已有的最大项目,那么您就知道当前数组中的其他项目不会更小。所以你可以跳过当前数组的其余部分。

这是为您提供最小k项的算法。如果您想要最大的k项,请构建MIN堆并将if (item < heap.peek())更改为if (item > heap.peek())。在这种情况下,通过向后移动数组可以获得更好的性能。这将减少堆插入和删除的数量。如果你不向后走数组,你将无法使用我展示的优化。

另一种方法是将所有项目连接成一个数组并使用Quickselect。 QuickSelect是一种O(n)算法。 Empirical evidence表明在k < .01*n时使用堆更快。否则,Quickselect更快。当然,您的里程可能会有所不同,并且必须从多个阵列创建单个阵列将为Quickselect增加处理和内存开销。