【问题标题】:Time complexity for merging two sorted arrays of size n and m合并两个大小为 n 和 m 的排序数组的时间复杂度
【发布时间】:2012-06-14 18:31:19
【问题描述】:

我只是想知道合并两个大小为 n 和 m 的排序数组的时间复杂度是多少,因为 n 总是大于 m

我正在考虑使用归并排序,我假设在这种情况下会消耗 O(log n+m)。

我不太擅长大哦之类的东西。请建议我这个问题的时间复杂度,如果有更好的解决问题的方法,请告诉我。

【问题讨论】:

    标签: sorting complexity-theory


    【解决方案1】:

    合并两个排序列表的时间绝对不是 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 逻辑变为注释并添加和等号以涵盖该情况。 不打算优化伪代码。

    【讨论】:

    • 取决于 m 是否在 Theta(n) 中,使 O(n+m) 比 O(m log n) 慢。我没有检查您的算法,但绝对可以在 O(n) 中完成,请参阅我对 Dmitri 答案的评论。
    • 我们是不是应该考虑这种情况:a[i] = b[j]?
    • 这个答案是错误的:O(n+m) 并不比 O(n log(m)) 好!很容易看出这些复杂性不是单调的。一方面,如果我们将 m = n^k 设置为 k 大,那么 O(n+m) 比 O(n log(m)) 差得多,而另一方面 O(n+m) 比 O( n log(m)) 当 m ~= n 时。 TAOCP 的 5.3.2 节对此进行了讨论,并提出了一些其他算法。
    • Harry 也许你错过了 OP 声明 n>m 的地方或两个数组已经排序的地方。正如 G. Bach 提到的那样,真正的大 O 实际上是 O(n),因为该符号通常将事情简化到该级别(即 n 和 n+m 都表示随着输入的增加而随时间线性增长,因此您使用更多是简单的表达)。
    【解决方案2】:

    我自己刚刚经历这个问题,大O不是O(m+n),实际上只是O(n)

    以下是伪代码中的原因: (注意:我编写这段代码是为了处理 m > nm == 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]),我们不知道,没关系。第一个循环将迭代直到 ij 等于它们各自数组的长度,然后接下来的两个 while 循环中只有一个会运行,直到 A 或 B 的其余部分添加到 C。

    所以可以看出你是如何以O(m+n) 结束的,但是,Big O 通常处理最坏情况运行时,因此我们知道我们将遍历长度为 的数组nm 多 n 次,无论数组中元素的构成如何,因此 m 将始终小于 n .所以我们去掉 m 得到O(n)

    这是因为 n 可以等于 infinity,因此既然有这种可能性,那么我们知道 m 永远不可能是一个事实无穷大,根据定义必须小于无穷大,因此进一步定义有一个小于n的常数(因为它停止增长到在某个点上是无限的,因为只有 n 可以是无限的)。由于我们在确定 Big O 运行时时删除了常量,因此我们得到了O(n)

    显然我迟到了几年,哈哈,但我希望这能为将来可能遇到这种情况的其他人扫清空气:)

    【讨论】:

    • 这是不正确的。为了合并两个数组,无论如何我们总是要遍历它们,所以迭代次数总是m+n。如果m=300n=500 根据你的说法。我们可以忽略m,因为它小于n,但这绝对不是真的,你仍然需要800 次迭代才能得到你的结果。你不能就这样忽视它。我们之所以说合并两个数组需要O(n) 时间,只是为了说明这个算法需要线性时间。
    【解决方案3】:

    注意!此答案包含错误

    存在一种更有效的算法,并在另一个答案中提出。


    复杂度为 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) )。如果存在不依赖于nm 的常量k&gt;1,则n &gt; k * m 则为O(log(n/m)) = O(log(n)),我们得到与上述相同的渐近复杂度。

    【讨论】:

    • 运算可以在 O(m+n) = O(n) 中完成,因为 m
    • 顺便说一句,很抱歉投反对票 - 我最初认为您的回答效率低下,但后来明白事实并非如此。 SO不会让我再次投票,除非答案被编辑;也许你可以这样做?
    • "因此总体复杂度为 O (m log n)。"插入包含 n 个元素的数组本身就是一个 O(n) 操作,因为每个元素都必须向右移动。那么上面引用的句子中的 O(m * log n) 实际上会是 O (mn log n) 吗?
    猜你喜欢
    • 2016-01-13
    • 1970-01-01
    • 2020-11-16
    • 2020-10-27
    • 2018-07-14
    • 2016-09-02
    • 2012-05-07
    • 1970-01-01
    • 2020-03-09
    相关资源
    最近更新 更多