我的算法教科书中有一个练习,我不太确定解决方案。我需要解释为什么这个解决方案:
function array_merge_sorted(array $foo, array $bar)
{
$baz = array_merge($foo, $bar);
$baz = array_unique($baz);
sort($baz);
return $baz;
}
合并两个数组并对它们进行排序并不是最有效的,我需要提供一个最优化的解决方案,并证明不能做更好的解决方案。
我的想法是使用一个O(n log n)的mergesort算法来合并和排序作为参数传递的两个数组。但是,我怎样才能证明这是最好的解决方案?
答案 0 :(得分:0)
正如您所说两个输入已经排序,您可以使用简单的拉链式方法。
每个输入数组都有一个指针,指向它的开头。然后比较两个元素,将较小的元素添加到结果中,并使用较小的元素推进数组的指针。然后重复该步骤,直到两个指针都到达结尾,并将所有元素添加到结果中。
您可以在Wikipedia#Merge algorithm找到此类算法的集合,目前显示的方法列为合并两个列表。
这是一些伪代码:
function Array<Element> mergeSorted(Array<Element> first, Array<Element> second) {
Array<Element> result = new Array<Element>(first.length + second.length);
int firstPointer = 0;
int secondPointer = 0;
while (firstPointer < first.length && secondPointer < first.length) {
Element elementOfFirst = first.get(firstPointer);
Element elementOfSecond = second.get(secondPointer);
if (elementOfFirst < elementOfSecond) {
result.add(elementOfFirst);
firstPointer = firstPointer + 1;
} else {
result.add(elementOfSecond);
secondPointer = secondPointer + 1;
}
}
}
该算法显然适用于O(n)
,其中n
是结果列表的大小。或者更精确的是O(max(n, n')
n
是第一个列表的大小,第二个列表的n'
(或O(n + n')
是相同的集合。)
这显然也是最佳,因为在某些时候,您需要至少遍历所有元素一次,以便构建结果并知道最终排序。对于此问题,这会产生Omega(n)
的下限,因此算法是最优的。
更正式的证明假设更好的任意算法A
解决了问题没有看一下每个元素至少一次(或更准确地说,小于O(n)
)。
我们将该算法不看的那个元素称为e
。我们现在可以构造一个输入I
,使得e
具有一个值,该值满足其自己的数组中的顺序,但算法会在结果数组中将其置错。
我们能够为每个算法A
执行此操作,并且由于A
始终需要在所有可能的输入上正常工作,我们能够找到一个反例I
,以便它失败了。
因此A
不存在,Omega(n)
是该问题的下限。
您的给定算法首先合并两个数组,这适用于O(n)
,这很好。但之后它对阵列进行了排序。
排序(更精确:基于比较的排序)的下限为Omega(n log n)
。这意味着每个这样的算法都不能比这更好。
因此,给定算法的总时间复杂度为O(n log n)
(因为排序部分)。哪个比 O(n)
更差,其他算法的复杂性以及最佳解决方案。
然而,为了超级正确,我们还需要争论 sort 方法是否真正产生了这种复杂性,因为它不会获得任意输入,但始终是的结果合并强> -method。因此,特定排序方法可能特别适用于此类特定输入,最终会产生O(n)
。
但我怀疑这是你工作的重点。