【问题标题】:Space requirements of a merge-sort合并排序的空间要求
【发布时间】:2011-02-27 08:54:48
【问题描述】:

我正在尝试了解合并排序的空间要求,O(n)。
我看到时间要求基本上是级别数(logn)* 合并(n),这样就产生了(n log n)。
现在,我们仍然在每个级别分配 n,在左右 2 个不同的数组中。
我明白这里的关键是当递归函数返回时,空间被释放,但我没有看到它太明显。
此外,我找到的所有信息,只是说明所需的空间是 O(n),但不要解释它。
有什么提示吗?

function merge_sort(m)
    if length(m) ≤ 1
        return m
    var list left, right, result
    var integer middle = length(m) / 2
    for each x in m up to middle
         add x to left
    for each x in m after middle
         add x to right
    left = merge_sort(left)
    right = merge_sort(right)
    result = merge(left, right)
    return result

编辑 好的,感谢@Uri,这就是诀窍
一开始没看到的是时间只加,而内存加减,所以最大时间是在执行结束时,但最大内存是在递归栈的底部。

所以,如果我们继续添加 n + n/2 + n/4 + n/8.... 不管我们添加多少次,它永远不会大于 2n,当我们达到递归堆栈底部并开始向上,我们不保留前一个分支使用的内存,所以在最大值,2n 将是使用的内存量,O(n)。

【问题讨论】:

    标签: algorithm sorting mergesort


    【解决方案1】:

    这是我对代码空间复杂度的解释。基本上,当算法得到结果时,我们在内存上的表现如何。

    1) 您进行的每个递归调用都分配了一个恒定大小的堆栈帧以及任何不是“n”函数的变量。 我们称这个常数为“c”。因为,你要深入 lg(n) 级,结果是 c*lg(n),即 O(lg(n))。

    2) 现在,当我们计算结果时,我们为 n/(2^k) 个元素分配空间,其中 k 是您所处的级别。

    left = merge_sort(left)
    right = merge_sort(right)
    

    对于可能想知道我们如何得出 n/(2^k) 的人,请注意,在求解数组的前半部分(即 left=merge_sort(left))时,我们首先要分配内存。一旦这个递归树结束,我们最终会释放所有内存并在求解右侧之前回到起点。因此,它的 n/(2^k)。 这将是 O(n)。

    3) 最后,合并过程也可能分配额外的空间(如果使用链表,则可能不需要这个空间),这是 O(n)

    最终答案 = O(lg(n)) + O(n) + O(n) 即 O(n)。

    【讨论】:

      【解决方案2】:

      有一些版本的合并排序可以就地工作。

      但是,在大多数实现中,空间在数组大小上是线性的。这意味着 n 表示第一级,n/2 表示第二级,n/4 表示第三级,等等。当你处于递归的底部时,这个系列加起来约为 2n,这是线性的。

      【讨论】:

      • @Arkaitz:“大多数实现”包括您发布的实现。
      • 我已经发布了我现在理解的内容,如果有错误请纠正我。
      • 您理解正确。问题在于伪代码,它更容易可视化控制(因此时间复杂度)而不是调用堆栈的内容。
      • @Uri 当您解释 (n, n/2, n/4,...) 的总和负责 N 运行时时,您是否使用创建的合并排序实现这样说每次当前帧调用排序时的数组较小的组件数组?
      • 如果不是真的,意味着每次都传递同一个数组进行排序,我的理解是,当一个合并原始的两半时,您将拥有的最大开销是在堆栈的顶部数组。
      猜你喜欢
      • 1970-01-01
      • 2012-05-07
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-18
      • 2021-08-04
      相关资源
      最近更新 更多