【发布时间】:2012-06-14 18:31:19
【问题描述】:
我只是想知道合并两个大小为 n 和 m 的排序数组的时间复杂度是多少,因为 n 总是大于 m。
我正在考虑使用归并排序,我假设在这种情况下会消耗 O(log n+m)。
我不太擅长大哦之类的东西。请建议我这个问题的时间复杂度,如果有更好的解决问题的方法,请告诉我。
【问题讨论】:
我只是想知道合并两个大小为 n 和 m 的排序数组的时间复杂度是多少,因为 n 总是大于 m。
我正在考虑使用归并排序,我假设在这种情况下会消耗 O(log n+m)。
我不太擅长大哦之类的东西。请建议我这个问题的时间复杂度,如果有更好的解决问题的方法,请告诉我。
【问题讨论】:
合并两个排序列表的时间绝对不是 O(m log n)。是 O(n+m)。
代码如下所示:
allocate c with length n+m
i = 1
j = 1
while i < n or j < m
if i = n
copy rest of b into c
break
if j = m
copy rest of a into c
break
if a[i] < b[j]
copy a[i] into c
i=i+1
continue
Else #b[j] =< a[i]
copy b[j] into c
j=j+1
continue
现在,如果我们没有足够的内存来分配 c,这可以修改为仍然是 O(n+m) 时间,因为大多数硬件(例如 RAM 和硬盘)都允许块操作。当我们需要将单个项目插入到数组的中间时,将块的尾部移到一个上方以腾出空间是一个单一的操作。如果您使用的硬件不允许这样做,那么每个插入可能需要 O(n),这将是 O(n+mn) 复杂度。由于我们只需要将较小数组的元素插入到较大数组中,因此当较大数组中的元素已经在正确的位置时,我们永远不需要移动较大数组的片段。因此,n 保持不变,增加了 m 位的复杂度。当所有长度为 m 的数组 b 正确地放置在长度为 n 的数组 a 前面时,这是最坏的情况。
编辑:将最后一个 if 更改为 else 并将原始 if 逻辑变为注释并添加和等号以涵盖该情况。 不打算优化伪代码。
【讨论】:
我自己刚刚经历这个问题,大O不是O(m+n),实际上只是O(n)。
以下是伪代码中的原因: (注意:我编写这段代码是为了处理 m > n 或 m == n 的情况)
Merging sorted arrays A and B into C.
let ints 'i' and 'j' and 'k' = 0
while(i < A.length && j < B.length){
if(A[i] < B[j]){
C[k] = A[i]
i++
} else {
C[k] = B[j]
j++
}
k++
}
// Copies rest of A into C if A.len > B.len
while(i < A.length){
C[k] = A[i]
k++
i++
}
// Copies rest of B into C if A.len < B.len
while(j < B.length){
C[k] = B[j]
k++
j++
}
return C
现在我们只知道长度为 n 的数组大于长度为 m 的数组。这意味着长度为 m 的数组中的每个元素都有可能大于长度为 n 的数组中的每个元素,即使 n > m >(如A[2,3,4,5,6]和B[7,8,9]),我们不知道,没关系。第一个循环将迭代直到 i 或 j 等于它们各自数组的长度,然后接下来的两个 while 循环中只有一个会运行,直到 A 或 B 的其余部分添加到 C。
所以可以看出你是如何以O(m+n) 结束的,但是,Big O 通常处理最坏情况运行时,因此我们知道我们将遍历长度为 的数组n 比 m 多 n 次,无论数组中元素的构成如何,因此 m 将始终小于 n .所以我们去掉 m 得到O(n)。
这是因为 n 可以等于 infinity,因此既然有这种可能性,那么我们知道 m 永远不可能是一个事实无穷大,根据定义必须小于无穷大,因此进一步定义有一个小于n的常数(因为它停止增长到在某个点上是无限的,因为只有 n 可以是无限的)。由于我们在确定 Big O 运行时时删除了常量,因此我们得到了O(n)。
显然我迟到了几年,哈哈,但我希望这能为将来可能遇到这种情况的其他人扫清空气:)
【讨论】:
m+n。如果m=300 和n=500 根据你的说法。我们可以忽略m,因为它小于n,但这绝对不是真的,你仍然需要800 次迭代才能得到你的结果。你不能就这样忽视它。我们之所以说合并两个数组需要O(n) 时间,只是为了说明这个算法需要线性时间。
注意!此答案包含错误
存在一种更有效的算法,并在另一个答案中提出。
复杂度为 O(m log n)。
设长数组为a,短数组为b,那么你描述的算法可以写成
for each x in b
insert x into a
循环有 m 次迭代。每次插入排序数组都是 O(log n) 操作。因此总体复杂度为 O (m log n)。
由于b已排序,上述算法可以更高效
for q from 1 to m
if q == 1 then insert b[q] into a
else
insert b[q] into a starting from the position of b[q-1]
这可以提供更好的渐近复杂度吗?不是真的。
假设来自b 的元素沿a 均匀分布。然后每次插入将采用O(log (n/m)),整体复杂度将为O(m log(n/m) )。如果存在不依赖于n 或m 的常量k>1,则n > k * m 则为O(log(n/m)) = O(log(n)),我们得到与上述相同的渐近复杂度。
【讨论】: