快速排序混淆需要对数空间

时间:2016-04-23 00:38:23

标签: java algorithm sorting big-o quicksort

如果只调用较小的分区数组,那么较大的分区数组是如何排序的?我只看到代码在递归完成时更改b的位置(QSif语句中调用else

 public static void QS(int[] b, int h, int k) {
     int h1= h; int k1= k;
     // invariant b[h..k] is sorted if b[h1..k1] is sorted
     while (b[h1..k1] has more than 1 element) {
         int j= partition(b, h1, k1);
         // b[h1..j-1] <= b[j] <= b[j+1..k1]
         if (b[h1..j-1] smaller than b[j+1..k1])
             QS(b, h, j-1); h1= j+1; 
         else
             QS(b, j+1, k1); k1= j-1; 
     }
}

2 个答案:

答案 0 :(得分:1)

注意在递归调用QS后,如果b [h1 ..]小于b [j + 1 ..]则更新h1,如果b [h1 ..]大于或等于b,则更新k1 b [j + 1 ..]。

代码中有一个错误,if之后的第一个调用应该是QS(b,h1,j-1);

对数空间使用是指由于递归而由quicksort使用的堆栈空间。在示例代码中,只有较小的分区使用递归调用进行排序,然后代码循环返回以将较大的分区分成两部分,并且再次仅对现在拆分的较大分区的较小部分使用递归调用

文章链接:

http://en.wikipedia.org/wiki/Quicksort#Optimizations

http://blogs.msdn.microsoft.com/devdev/2006/01/18/efficient-selection-and-partial-sorting-based-on-quicksort

我不确定对尾递归的引用,因为代码包含一个实际的循环而不是尾递归。尾递归看起来像是在函数中执行的最后一行的递归调用,编译器可以将其优化为循环。

答案 1 :(得分:1)

这是一些难以阅读的伪代码。这可能更容易理解:

QuickSort(b[], low, hi)
  while the range low..hi contains more than 1 element
    1: Pick a random pivot 'j' from the array
    2: Re-order the array so that all elements less than the pivot are in
       b[low..j-1], and all elements greater than the pivot are in b[j+1..hi]
    3: Call quicksort on the side with less elements, and update the range to
       exclude the sorted side

大约一半的值将小于数据透视表,并且一半的值将大于数据透视表。这意味着在步骤3之后,范围 .. hi 的大小大致减半。因此,它需要log | N |范围之前的迭代只包含一个元素。

很难解释这一点,但是看看第3步如何仅在阵列的一半上调用QuickSort?这是因为while循环的其余部分对另一半进行排序。该功能可以轻松地重写为以下内容:

QuickSort(b[], low, hi)
  if the range low..hi contains more than 1 element
    1: Pick a random pivot 'j' from the array
    2: Re-order the array so that all elements less than the pivot are in
       b[low..j-1], and all elements greater than the pivot are in b[j+1..hi]
    3: Call quicksort on both sides

while循环已被if语句和第二个递归调用替换。我希望从这里你可以看到复杂性大致为N log | N |。

修改

那么while循环如何对其余元素进行排序?在步骤3之后,范围已更新以排除较小的一半,因为我们只是通过调用QuickSort对其进行排序。这意味着该范围现在只包含较大的一半 - 未排序的元素。因此,我们对这些未排序的元素重复步骤1 - 3,然后再次更新范围。

每次迭代时,未排序元素的数量越来越小,最终我们只剩下一个未排序元素。但是,当然,其中的一个元素是排序的,所以此时我们知道我们已经对数组中的每个元素进行了排序。