为啥基数排序的空间复杂度为 O(k + n)?

Posted

技术标签:

【中文标题】为啥基数排序的空间复杂度为 O(k + n)?【英文标题】:Why does radix sort have a space complexity of O(k + n)?为什么基数排序的空间复杂度为 O(k + n)? 【发布时间】:2017-11-12 15:55:01 【问题描述】:

考虑一个包含n 数字的数组,该数组具有最大k 数字(请参阅编辑)。考虑here的基数排序程序:

def radixsort( aList ):
  RADIX = 10
  maxLength = False
  tmp, placement = -1, 1

  while not maxLength:
    maxLength = True
    # declare and initialize buckets
    buckets = [list() for _ in range( RADIX )]

    # split aList between lists
    for  i in aList:
      tmp = i / placement
      buckets[tmp % RADIX].append( i )
      if maxLength and tmp > 0:
        maxLength = False

    # empty lists into aList array
    a = 0
    for b in range( RADIX ):
      buck = buckets[b]
      for i in buck:
        aList[a] = i
        a += 1

    # move to next digit
    placement *= RADIX

buckets 基本上是所有数字的二维列表。但是,只会将 n 值添加到其中。为什么空间复杂度是 O(k + n) 而不是 O(n)?如果我错了,请纠正我,即使我们考虑用于在特定位置提取数字的空间,它也只使用 1(常量)内存空间?

编辑:我想解释一下我对k的理解。假设我输入[12, 13, 65, 32, 789, 1, 3],链接中给出的算法将经过4遍(函数内的第一个while循环)。这里k = 4,即最大数量。数组中任何元素的位数 + 1。因此 k 不是。通行证。这与该算法的时间复杂度所涉及的 k 相同:O(kn) 这是有道理的。我无法理解它在空间复杂性中的作用:O(k + n)

【问题讨论】:

【参考方案1】:

基数排序的空间复杂度与它用于对每个基数进行排序的排序有关。最好的情况是计数排序。

这是CLRS提供的用于计数排序的伪代码:

Counting-sort(A,B,k)
  let C[0..k] be a new array
  for i = 0 to k
      C[i] = o
  for j = 1 to A.length
      C[A[j]] = C[A[j]] + 1
  for i = 1 to k
      C[i] = C[i] + C[i-1]
  for j = A.length down to 1
      B[C[A[j]]] = A[j]
      C[A[j]] = C[A[j]] - 1 

如您所见,计数排序会创建多个数组,一个基于 K 的大小,一个基于 N 的大小。B 是大小为 n 的输出数组。 C 是一个大小为 k 的辅助数组。

因为基数排序使用计数排序,所以计数排序的空间复杂度是基数排序空间复杂度的下限。

【讨论】:

这完全是错误的。 Here,例如,您可以找到我的就地基数排序实现,其空间复杂度为O(1)。更重要的是,这个答案实际上并没有回答问题。 如果算法采用可变大小的输入,它就不可能有 O(1) 的空间复杂度。空间复杂度是衡量算法需要的工作存储量的指标。如果算法对数组进行操作,则需要该数组作为存储。就地并不意味着 O(1)。 另外,我只是在这里引用 CLRS。如果您能证明这些人是错的,那么我认为您将获得比堆栈溢出代表更多的收益。 嗯。我错了额外空间复杂度≠实际空间复杂度。我现在明白你的意思了; +1。 很抱歉延迟接受答案。我现在才满意地理解了这一点。【参考方案2】:

我认为存在术语问题。 Jayson Boubin's answer中提到的问题的实现和实现的空间复杂度是O(n+k)。但k 不是最长单词(或最长数字)的长度。 k 是“字母”的大小:不同数字(数字)或字母(单词)的数量。

buckets = [list() for _ in range( RADIX )]

此代码创建一个包含RADIX 元素的数组。在这个特定的实现中,RADIX 是一个常数(空间复杂度为 O(n)),但一般来说,它是一个变量。 RADIXk,不同数字(字母表中的字母)的数量。而这个k不依赖于n,在某些情况下可以大于n,所以空间复杂度一般为O(n+k)。

编辑:在this 实现中,placement(或tmp)的大小是O(k)(使用您对k 的定义),因为k 是@987654339 @base 10,而placement 大小为log(maxNumber) base 256。但我不确定这是一般情况。

【讨论】:

是的。 k 表示数字,如果是数字。 我不相信 k 与基数或其长度相同。基数始终为 10 。但如果至少有一个三位数,则 k 将为 3,其余为 2 或 1 位数。 @skr_robo,这就是我要说的,在O(n+k) k 不是某个数字中的最大位数。 @DAIe 如果我们输入这个数组 = [12, 13, 65, 32, 789, 1, 3],上述算法将运行 4 遍(因为有一个 3 位数字)。现在,时间复杂度是 O(kn),这个 k 是通过的次数,即 4。我的观点是 O(k+n) 也有相同的 k。我不明白为什么基数在这里起作用? 我在调用k 最大位数时出错了。它不止于此。【参考方案3】:

基数排序对数据集中的每一位数字使用计数排序。计数排序的空间复杂度为 O(n+k),其中 k 是数据集中的最大数。

十进制数字的范围从 0 到 9,所以如果我们使用基数排序(基数排序中使用的计数排序)对 4 个十进制数(11,22,88,99)进行排序,对于每个数字,它将创建大小为 b = 10 其中 b 是底数。

这意味着使用的总空间将是总位数 * (n + base)。如果总位数不变。空间复杂度变为 O(n+base)。

因此基数排序的空间复杂度为 O(n+b)。

【讨论】:

以上是关于为啥基数排序的空间复杂度为 O(k + n)?的主要内容,如果未能解决你的问题,请参考以下文章

基数排序

交换与选择类排序

基数排序与基数排序

计数排序基数排序桶排序

基数排序

排序算法时间复杂度为O(n²)的排序算法