合并具有最少比较数的两个排序数组的算法


24

给定两个大小为nm的类型为T的排序数组ab。我正在寻找一种将两个数组合并为一个新数组(最大大小为n + m)的算法。

如果比较便宜的话,这很简单。只需从具有最低第一个元素的数组中取出,直到完全遍历一个或两个数组,然后添加其余元素即可。像这样的东西/programming/5958169/how-to-merge-two-sorted-arrays-into-a-sorted-array

但是,当比较两个元素时,情况的改变要比将元素从源数组复制到目标数组要贵得多。例如,您可能具有大的任意精度整数或字符串组成的数组,其中比较可能会非常昂贵。只需假设创建数组和复制元素是免费的,而唯一花​​费的就是比较元素。

在这种情况下,您希望使用最少数量的元素比较合并两个数组。这里有一些示例,您应该可以比简单的合并算法做得更好:

a = [1,2,3,4, ... 1000]
b = [1001,1002,1003,1004, ... 2000]

要么

a = [1,2,3,4, ... 1000]
b = [0,100,200, ... 1000]

在某些情况下,简单合并算法将是最佳选择,例如

a = [1,3,5,7,9,....,999]
b = [2,4,6,8,10,....,1000]

因此,理想情况下,算法应该适当地降级并在阵列交错或至少不会明显恶化的情况下执行最多n + m-1个比较。

对于大小差异较大的列表,应该做得很好的一件事是使用二进制搜索将较小数组的元素插入较大数组。但是,如果两个列表的大小相同且交织在一起,则不会优雅地降低性能。

元素唯一可用的是(总计)排序功能,因此任何使比较便宜的方案都是不可能的。

有任何想法吗?

在Scala中提出了这一点。我认为比较次数是最佳的,但是我无法证明这一点。至少它比我在文献中发现的简单得多。

而且自原始帖子以来,我写了一篇有关其工作原理的博客文章


2
没有比“简单合并算法”更少的比较的方法了。您可以尝试像您提到的第一个那样处理边缘情况,但这会使平均情况恶化。
Mephy 2014年

5
@Mephy:请赐教并给我们正式证明。或者,如果不能,请考虑删除(或至少优化)您的评论。
Doc Brown

4
@DocBrown如果我有正式证明,我将给出答案,而不是评论。无论如何,这是一个非常明显的线性问题,因为试图找到一个优于线性的解决方案至少需要线性时间。
Mephy 2014年

4
@Mephy:我建议您花些时间阅读下面的答案,并三思而后行。
布朗

4
@Mephy大多数显而易见的事情(“您不能在小于O(n ^ 2)的情况下进行乘法”,“如果我改变选择的门,就不会提高赢得价格的机会”,“您可以't排序小于O(n log n)“,..)是错误的。例如,在较短的列表上使用二进制搜索方法应该可以改善平均情况。
Voo

Answers:


31

常规合并排序算法-合并步骤通常应用n + m -1比较,其中一个列表的大小为n,另一个列表的大小为m。使用此算法是合并两个排序列表的最简单方法。

如果比较成本太高,您可以做两件事-尽量减少比较次数或最小化比较成本。

让我们专注于最小化比较成本。您和您自己可以决定比较的数据是否可以量化。如果可以量化它们,则这是实现哈希方法的一种形式,它保持了顺序。例如,如果您的数据按名称进行比较,则第一个tname,...您可以将第一个带到名称为“ Klaehn,Ruediger”的字符,然后将数据元素简化/量化为“ Kl.Ru”(如果进行比较)到“ Packer,The”,您保留顺序“ Pa.Th”-您现在可以应用更便宜的比较算法,比较减小的值。但是,如果您找到另一个“ Kl.Ru”,那么您现在的价值就差不多了,现在您可能会转向比较这些元素的更昂贵的方法。

如果您可以从数据中提取此量化值,并且比比较它更快,那么这就是您要做的第一件事,首先比较量化值或哈希值。请记住,该值仅需计算一次,因此您可以在创建数据元素时对其进行计算。

我还提到了另一种方法,以最大程度地减少您的比较。

我浏览了经典的书籍TAOCP-第3卷,排序和搜索(第197-207页,第5.3.2节),其中有10页涉及该主题。我发现有两个比n + m-1比较快的算法参考。

首先是Hwang-Lin合并算法,其次是Glenn K Manacher的改进-TAOCP和Christen均引用了该算法,该算法在特殊条件下对长度n和m进行了接近所需比较的下限。列表中。

Manacher的算法已在Journal of the ACM Vol。26第434-440页上的3:“对“焕林”合并算法的重大改进”。具有m个项目的列表和具有n个项目的列表可以具有不同的长度,但是它们还必须由它们包含m <= n的元素数来表示

Hwang-Lin算法将要合并的列表分解成较小的列表,并通过比较每个子列表的第一个元素来对列表进行排序,并确定是否需要比较子列表中的某些元素。如果第一个列表小于第二个列表,则机会很大,较长列表的连续元素可以不进行比较而转移到结果列表中。如果small ist的第一个元素大于拆分的较大列表的第一个元素,则可以复制子列表前面的所有元素而无需比较。

在第2节中对Hwang和Lin(Vega,Frieze,Santha)合并算法的平均案例分析中,您可以找到HL-Algorithm的伪代码。这比我的描述要好得多。您会看到为什么比较次数较少-该算法使用二进制搜索来查找索引,并从较短的列表中插入元素。

如果列表没有像您上一个示例那样交错,则在大多数情况下,您应该有一个较小的列表和一个较大的列表。这是HL算法开始表现更好的时候。


谢谢您对此发表的评论-我检查了答案,发现Knuth在此主题上花了整整10页。然后,我从m个书架上拿走了JACM,然后再往前看。我会改善答案。-无需投票。哈希(量化器)算法是一个简单的想法,可以应用于许多数据集-但是只有提出要求的Guy才是唯一决定该算法是否适用于他的数据的人。
thepacker 2014年

4
在您改善答案之后,所有投票否决您的人都将有机会再次投票赞成您;-)
Doc Brown

+1表示,如果大小差异很大,则标准合并不是最佳的。
Florian F

1

假设两个数组具有N和M个元素,N≥M,并且所有元素都不相同。

如果排序后的数组包含元素N的x以及元素M的y,反之亦然,则必须对x和y进行比较,否则我们将不知道它们属于哪个顺序。(例如,不能知道x <a <b <c <y的其他元素,例如a,b,c,因为在x和y之间没有元素。因此必须将x和y进行比较直。

如果N> M,则可能有一个数组,其中M的每个元素都在N的元素之前和之后,这意味着至少需要进行2M比较-即使您使用非确定性排序算法也可以完美猜测要比较的数字。(这意味着:假设您有N个大数,M =1。二进制搜索需要O(log2 N)个步骤;一种不确定性算法将猜测第二个数组的一个元素在哪个元素之间,并对两个元素进行两次比较。确认猜测)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.