如何使用MERGE SORT对K个已排序的数组进行排序

时间:2013-09-24 11:49:21

标签: algorithm sorting

我知道这个问题已被提出,并且使用最小堆有一个非常好的优雅解决方案。

我的问题是如何使用合并排序的合并功能来做到这一点。

您已经有一组已排序的数组。所以你应该能够在O(nlog K)时间内将它们全部合并到一个数组中,对吗?

我只是想不通怎么做!

说我有

[[5,6],[3,4],[1,2],[0]]

第1步:[[3,4,5,6],[0,1,2]]

第2步:[[0,1,2,3,4,5,6]]

有一种简单的方法吗? O(nlog K)理论上是否可以通过mergesort实现?

5 个答案:

答案 0 :(得分:12)

正如其他人所说,使用最小堆来保存下一个项目是最佳方式。它被称为N路合并。它的复杂性是O(n log k)。

可以使用双向合并算法对k阵列进行排序。也许最简单的方法是修改标准合并排序,以便它使用非常量分区大小。例如,假设您有4个长度为10,8,12和33的数组。每个数组都已排序。如果将数组连接成一个,则会有这些分区(数字是数组的索引,而不是值):

[0-9][10-17][18-29][30-62]

合并排序的第一遍将具有0和10的起始索引。您可以将其合并到新数组中,就像使用标准合并排序一样。下一遍将从第二个阵列中的位置18和30开始。完成第二遍后,输出数组包含:

[0-17][18-62]

现在你的分区从0和18开始。你将这两个合并到一个数组中就完成了。

唯一真正的区别是,不是以分区大小为2并且加倍,而是具有非常量分区大小。在进行每次传递时,新分区大小是您在上一次传递中使用的两个分区的大小之和。这实际上只是对标准合并排序的一点修改。

需要使用log(k)传递来进行排序,并在每次传递时查看所有n个项目。该算法为O(n log k),但具有比N路合并高得多的常数。

实现时,构建一个包含每个子数组的起始索引的整数数组。所以在上面的例子中你会得到:

int[] partitions = [0, 10, 18, 30];
int numPartitions = 4;

现在进行标准合并排序。但是您从partitions阵列中选择了分区。所以你的合并将从以下开始:

merge (inputArray, outputArray, part1Index, part2Index, outputStart)
{
    part1Start = partitions[part1Index];
    part2Start = partitions[part2Index];

    part1Length = part2Start - part1Start;
    part2Length = partitions[part2Index-1] - part2Start;

    // now merge part1 and part2 into the output array,
    // starting at outputStart
}

你的主循环看起来像是:

while (numPartitions > 1)
{
    for (int p = 0; p < numPartitions; p += 2)
    {
        outputStart = partitions[p];
        merge(inputArray, outputArray, p, p+1, outputStart);
        // update partitions table
        partitions[p/2] = partitions[p] + partitions[p+1];
    }
    numPartitions /= 2;
}

这是基本的想法。当数字是奇数时,你将不得不做一些工作来处理悬空分区,但总的来说就是它是如何完成的。

您也可以通过维护一个数组数组,并将每两个数组合并为一个新数组,并将其添加到数组的输出数组中来实现。泡沫,冲洗,重复。

答案 1 :(得分:5)

你应该注意到,当我们说复杂度为O(n log k)时,我们假设n表示所有k个数组中的元素总数,即最终合并数组中的元素数。

例如,如果要合并每个包含n个元素的k个数组,则最终数组中的元素总数将为nk。所以复杂性将是O(nk log k)。

答案 2 :(得分:2)

有不同的方法来合并数组。要在N * Log(K)时间内完成该任务,您可以使用名为Heap的结构(实现优先级队列是一种很好的结构)。我想你已经拥有它,如果你没有,那么请选择任何可用的实现:http://en.wikipedia.org/wiki/Heap_(data_structure) 然后你就可以这样做:

1.  We have A[1..K] array of arrays to sort, Head[1..K] - current pointer for every array and Count[1..K] - number of items for every array.
2.  We have Heap of pairs (Value: int; NumberOfArray: int) - empty at start.
3.  We put to the heap first item of every array - initialization phase.
4.  Then we organize cycle:
5.  Get pair (Value, NumberOfArray) from the heap. 
6.  Value is next value to output.
7.  NumberOfArray – is number of array where we need to take next item (if any) and place to the heap.
8.  If heap is not empty, then repeat from step 5

因此,对于每个项目,我们只使用从K项目构建的堆作为最大值。这意味着我们会根据您的要求提供N * Log(K)复杂度。

答案 3 :(得分:1)

我在python中实现了它。主要思想与mergesort类似。 列表中有k个数组。在函数mainMerageK中,只需将列表(k)划分为左(k / 2)和右(k / 2)。因此,分区的总数是log(k)。关于函数合并,很容易知道运行时是O(n)。最后,我们得到O(n log k) 顺便说一下,它也可以在min heap中实现,并且有一个链接:Merging K- Sorted Lists using Priority Queue

def mainMergeK(*lists):
    # implemented by k-way partition
    k = len(lists)
    if k > 1:
        mid = int(k / 2)
        B = mainMergeK(*lists[0: mid])
        C = mainMergeK(*lists[mid:])
        A = merge(B, C)
        print B, ' + ', C, ' = ', A
        return A
    return lists[0]

def merge(B, C):
    A = []
    p = len(B)
    q = len(C)
    i = 0
    j = 0
    while i < p and j < q:
        if B[i] <= C[j]:
            A.append(B[i])
            i += 1
        else:
            A.append(C[j])
            j += 1
    if i == p:
        for c in C[j:]:
            A.append(c)
    else:
        for b in B[i:]:
            A.append(b)
    return A

if __name__ == '__main__':
    x = mainMergeK([1, 3, 5], [2, 4, 6], [7, 8, 10], [9])
    print x

输出如下:

[1, 3, 5]  +  [2, 4, 6]  =  [1, 2, 3, 4, 5, 6]
[7, 8, 10]  +  [9]  =  [7, 8, 9, 10]
[1, 2, 3, 4, 5, 6]  +  [7, 8, 9, 10]  =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

答案 4 :(得分:1)

除了K项之外,就像双向合并一样。将导致O(NK)。如果你想要O(N logK),你将需要使用最小堆来跟踪下面算法中的K指针(源数组作为元数据):

保留一个K元素数组 - 即K指针显示每个数组中的位置。 标记所有K个元素都有效。

循环: 比较K指针中有效的值。如果值为最小值,请选择最小索引指针并将其增加到数组中的下一个值。如果递增的值已超过其数组,则将其标记为无效。 将最小值添加到结果中。 重复直到所有K元素都无效。

例如:

Positions        Arrays
p1:0  Array 1:  0  5  10  
p2:3  Array 2:  3  6   9
p3:2  Array 3:  2  4  6

输出(min为0,3,2)=&gt; 0.因此输出为{0}

      Array
p1:5    0  5  10
p2:3    3  6   9
p3:2    2  4  6

输出(最小值为5,3,2)=&gt; 2.所以{0,2}

       Array
p1:5    0  5  10
p2:3    3  6  9
p3:4    2  4  6

输出(最小值为5,3,4)=&gt; 3。所以{0,2,3} ..等等..直到你来到一个输出为{0,2,3,4,5,6}

的状态
   Array
p1:5    0  5  10
p2:9    3  6  9
p3:6    2  4  6

输出(min为5,9,6)=&gt; 6。所以{0,2,3,4,5,6} + {6}当你将数据标记为“无效”时将p3标记为“无效”。 (或者如果你使用的是min-heap,你只需删除min-item,获取它的源数组元数据:在本例中为数组3,看看它已经完成,所以你不会在min-heap中添加任何新内容)

相关问题