合并k个排序的数组-优先级队列与传统合并排序合并,什么时候使用?

时间:2018-11-17 22:10:17

标签: algorithm sorting merge divide-and-conquer external-sorting

假设我们得到了k个排序数组(每个数组的大小为n),在这种情况下,它使用的是 priority堆优于传统的合并(类似于一个)用于合并排序),反之亦然?

优先队列方法::在这种方法中,我们有一个大小为k min堆(最初,每个数组中的第一个元素被添加到堆中)。现在,我们从输入数组之一中删除min元素,将其放在最终数组中,然后从同一输入数组中插入一个新元素。这种方法需要O(kn log k)时间和O(kn)空间。注意:占用O(kn)空间是因为这是最终数组的大小,并且在计算渐近空间复杂度时,它占据了堆的大小。

传统合并::在这种方法中,我们合并前两个数组以获得大小为2n的排序数组。我们对所有输入数组重复此操作,并在第一遍之后获得k/2个排序数组,每个数组的大小为2n。我们重复此过程,直到获得最终阵列。每次通过的时间复杂度为O(kn),因为每次比较后都会将一个元素添加到相应的输出数组中。而且我们有log k个传递。因此,总时间复杂度为O(kn log k)。而且由于我们可以在每次通过之后删除输入数组,所以在任何时候使用的空间都是O(kn)

我们可以看到,两种方法的渐进时间和空间复杂度完全相同。那么,到底什么时候我们更喜欢另一种呢?我知道对于外部排序, Priority Queue 方法更好,因为您只需要O(k)内存空间,并且可以读写磁盘中的每个元素。但是,当我们有足够的内存时,这些方法又如何相互叠加?

1 个答案:

答案 0 :(得分:2)

比较+移动的操作总数大约是相同的。 k向合并的比较更多,但移动较少。我的系统有一个8路缓存(Intel 3770K 3.5 ghz),在4路合并排序的情况下,允许4行输入缓存4行,合并输出运行缓存1行。在64位模式下,有16个寄存器可用于工作变量,其中8个用于指向每个“运行”的当前位置和结束位置的指针(编译器优化)。

在我的系统上,我比较了4路合并(无堆,每移动一个元素〜3个比较)和2路合并(每移动〜1个比较,但通过次数为两倍),这4次合并的1.5倍比较次数,但移动次数为0.5倍,因此操作数基本相同,但由于缓存问题,第4种方式的速度大约提高了15%。

我不知道16个寄存器是否足以使6路合并更快一点,而16个寄存器是否不足以使8路合并(某些工作变量将基于内存/缓存)。尝试使用堆可能无济于事,因为堆将基于内存/缓存(而非基于寄存器)。

k合并对于外部排序最有用,由于移动开销较大,因此忽略了比较时间。