合并算法将两个已排序的输入数组合并为一个已排序的输出数组,方法是重复比较两个输入数组中的最小元素,并将两者中较小的一个移动到输出中。
现在我们需要将三个相同长度的排序输入数组(A1,A2和A3)合并到一个(排序的)输出数组中,有两种方法:
使用上述合并算法将A1和A2合并为A4,然后使用相同的算法将A4和A3合并到输出数组中。
修改上述合并算法,重复比较三个输入数组的最小元素,并将三者中最小的一个移动到输出数组。
如果仅考虑阵列元素移动的最坏情况(即分配),上述两种算法中哪一种更有效?
如果只考虑阵列元素比较的最坏情况,上述两种算法中的哪一种更有效?
在这两种算法之间,哪种算法在最坏的情况下具有更高的整体效率?
答案 0 :(得分:2)
如果您关心的只是数组写入的数量,则第二个版本(三向合并)比第一个算法(两个合并的两个实例)更快。三向合并算法将完成3n次写入(其中n是任何序列的长度),因为它在一次传递中合并所有三个范围。第一种方法将两个范围合并在一起,进行2n次写入,然后将该序列与第三个序列合并,进行3n次写入,总计5n次写入。
更一般地说,假设你有k个元素范围,全长为n。如果你成对地合并这些范围,然后再次成对合并那些合并等,那么你将合并大约k / 2合并步骤合并长度为n的范围,然后k / 4合并长度为2n的范围,然后k / 8合并长度4n等。这给出了总和
kn / 2 + kn / 2 + ... + kn / 2(log n次)
对于O(kn lg n)的净数组写入。另一方面,如果你在每一步使用k方式比较,那么你就可以完全执行kn写操作。
现在,让我们考虑一下您在每个设置中进行的比较。在三向合并中,写入输出序列的每个元素都需要找到最少三个值。这需要两个比较 - 一个用于比较前两个序列的第一个值,另一个用于比较这两个值的最小值与第三个数组的第一个值。因此,对于写入结果序列的每个值,我们使用两个比较,并且由于写入3n个值,我们需要总共进行最多6n个比较。
更好的方法是将序列存储在最小堆中,其中序列通过它们的第一个元素进行比较。在每一步中,我们将序列从具有最小第一个值的堆中出列,将该值写入结果,然后将序列的其余部分排入堆中。对于k个序列,这意味着写出的每个元素最多需要O(lg k)比较,因为堆插入在O(lg k)中运行。这给出了一个O(kn lg k)的净运行时间,因为写出的每个kn元素都需要O(lg k)处理时间。
在另一个版本中,我们首先进行标准的双向合并,这需要对每个元素进行一次比较,总计2n次比较。在合并的第二轮中,在最坏的情况下,我们总共进行3n次比较,因为有3G元素被合并。这总共提供了5n个比较。如果我们使用上面描述的成对合并的广义构造,我们将需要使用O(kn lg n)比较,因为写入的每个元素需要一次比较,我们做O(kn lg n)次写入。
简而言之,对于k = 3的特定情况,我们认为三向合并执行3n次写入和6n次比较,以实现9n内存读取和写入的净重。迭代的双向合并执行5n次写入和5n次比较,总共10n次内存读取和写入,因此三向合并版本更好。
如果我们考虑广义结构,则k-way合并执行O(nk)写入和O(nk lg k)比较以获得总共O(nk lg k)个存储器操作。迭代的双向合并算法对总共O(nk lg n)个存储器操作进行O(nk lg n)次写入和O(nk lg n)比较。因此,对于一些长序列,k-way合并渐近更好,而对于许多短序列,迭代合并排序更快。
希望这有帮助!