尾递归快速排序的空间复杂性是什么?

时间:2018-10-16 16:08:42

标签: algorithm quicksort partitioning space-complexity

查看以下尾部递归quicksort伪代码

QuickSort(A[1, ..., n], lo, hi)
Input: An array A of n distinct integers, the lower index and the higher index
         // For the first call lo = 1 and hi = n
Output: The array A in sorted order

If lo = hi return
         // The array A is already sorted in this case
If lo > hi or indices out of the range 1 to n then return

Else
      Pick an index k in [lo,hi] as the pivot
              // Assume that this can be done using O(1) cells on the stack
      i = Partition(A[lo, ..., hi], k)
              // Use in-place partitioning here so assume that this can be done
              // using O(1) space on the stack

If i - lo <= hi - i
      QuickSort(A, lo, i-1) // sort the smaller half first
      QuickSort(A, i+1, hi)
Else
      QuickSort(A, i+1, hi) // sort the smaller half first
      QuickSort(A, lo, i-1)

假设我每次分析枢轴时都经过对抗性选择,那么它的空间复杂度应为O(logn)[我不确定是完全正确的],但是如果枢轴为零,那么空间复杂度将受到怎样的影响随机选择统一?我对了解空间复杂度而不是时间复杂度还很陌生,因此感谢您提供任何反馈意见!

2 个答案:

答案 0 :(得分:0)

请参阅这篇涵盖Tail Recursion

的文章

在文章中,它表示尾递归快速排序的空间复杂度如下:

space complexity = input + O(log(n))

可以在下面找到一些更深入了解的文章:

答案 1 :(得分:0)

时间的最坏情况是,如果将数组划分得尽可能不均匀,那么该时间将为O(n^2)。如果您不执行尾递归,那对于空间来说也是最糟糕的情况。

但是,如果您不均匀地划分数组并进行尾递归排序,则对较大的一半进行排序的调用不会占用任何空间,因为您只需替换当前的调用框架即可。因此,使用的最大空间是当您一次又一次地进行第一次递归调用时。在最多1/2个呼叫帧中,最多... 1/2个中的log_2(n)个。

如果使用统一选择的枢轴从最坏情况切换到一般情况,则再次为O(log(n)),但常数更好。首先,它不能超过此范围,因为平均情况不能超过最坏情况。

诀窍是证明您无法提高界限。为了证明这一点,我们可以证明对大小为n的数组进行排序的平均空间至少为C log(n+1)/(3 log(2)),其中C是单个调用的空间。

通过检查,对于n = 1, 2, ..., 7来说确实如此,因为初始调用占用了空间Clog(n+1)/(3 log(2)) <= 1

如果n大于7,并且对n为止的语句都是正确的,则我们的透视图会将我们分成大小为mn-m的组,其中{{1 }}。至少有m <= n-m的赔率,而且我们在第一个递归调用中的预期最高费用至少为

n <= 4m

剩余的剩余时间和我们在尾部递归调用中的预期最大费用至少为

C 1 + f(m)
  >= C  + f(n/4 rounded up)
  >= C (3 log(2)) /(3 log(2))    + C log(n/4 + 1)/(3 log(2)))
  >  C (3 log(2)                 + log(n+1) - 2 log(2)    ) / (3 log(2)) )
   = C (log(n+1) + log(2)) / (3 log(2))

将这两个值取平均值时,将得到所需的f(n-m) >= f(n/2 rounded down) >= C log(n/2 + 1/2) / (3 log(2)) # n/2 = C (log(n+1) - log(2)) / (3 log(2)) 下限。

(我可能犯了一个小错误,但是这个想法是正确的。)