合并排序究竟有多少比较?

时间:2011-12-16 14:25:24

标签: algorithm sorting complexity-theory quicksort mergesort

我已经读过,quicksort在实践中比mergesort快得多,其原因是隐藏的常量。 那么,随机快速排序复杂度的解决方案是2nlnn = 1.39nlogn,这意味着快速排序中的常数是1.39。 但是mergesort怎么样? mergesort中的常量是什么?

6 个答案:

答案 0 :(得分:17)

让我们看看我们是否可以解决这个问题!

在合并排序中,在递归的每个级别,我们执行以下操作:

  1. 将阵列分成两半。
  2. 递归排序每一半。
  3. 使用合并算法将两半合并在一起。
  4. 那么每一步都进行了多少次比较?那么,除法步骤不进行任何比较;它只是将数组分成两半。第2步没有(直接)进行任何比较;所有比较都是通过递归调用完成的。在第3步中,我们有两个大小为n / 2的数组,需要合并它们。这需要最多n个比较,因为合并算法的每个步骤都进行比较然后消耗一些数组元素,所以我们不能做n次比较。

    将这些结合在一起,我们得到以下重复:

    C(1) = 0
    C(n) = 2C(n / 2) + n
    

    (正如评论中所提到的,线性项更准确地说是(n - 1),虽然这不会改变总体结论。我们将使用上述重复作为上限。)

    为了简化这一点,让我们定义n = 2 k 并用k来重写这种重复:

    C'(0) = 0
    C'(k) = 2C'(k - 1) + 2^k
    

    这里的前几个术语是0,2,8,24,....这看起来像k 2 k ,我们可以通过归纳来证明这一点。作为我们的基本情况,当k = 0时,第一项为0,并且k 2 k 的值也为0.对于归纳步​​骤,假设声明适用于某些k并考虑k + 1.然后该值为2(k 2 k )+ 2 k + 1 = k 2 k + 1 + 2 k + 1 =(k + 1)2 k + 1 ,因此声明适用于k + 1,完成归纳。因此,C'(k)的值是k 2 k 。由于n = 2 k ,这意味着,假设n是2的完美幂,我们得到的比较数是

      

    C(n)= n lg n

    令人印象深刻的是,这比更好 !那么为什么在地球上快速排序比合并排序更快?这与其他与比较次数无关的因素有关。首先,由于快速排序在合并排序不合适的情况下正常工作,因此合并排序中的引用位置与快速排序中的位置不同。这是一个非常重要的因素,因为快速排序在实践中比合并排序要好得多,因为高速缓存未命中的成本非常高。此外,对数组进行排序所需的时间不仅要考虑比较次数。其他因素,例如每个数组元素移动的次数也很重要。例如,在合并排序中,我们需要为缓冲元素分配空间,移动元素以便它们可以合并,然后合并回数组。我们的分析中没有计算这些动作,但它们肯定会加起来。将此与quicksort的分区步骤进行比较,该步骤将每个数组元素移动一次并保持在原始数组中。这些额外的因素,而不是所做的比较次数,在算法的运行时间占主导地位。

    这个分析比最优分析精确得有点精确,但Wikipedia证实分析大致为n lg n,这确实比quicksort的平均情况下的比较少。

    希望这有帮助!

答案 1 :(得分:3)

在最糟糕的情况下,假设直接实现,对 n 元素进行排序的比较次数为

n ⌈lg n 2 - 2 ⌈lg n + 1

其中lg n 表示 n base-2 logarithm

这个结果可以在Donald Knuth的the corresponding Wikipedia article或最新版本的The Art of Computer Programming中找到,我刚刚写下了this answer的证据。

答案 2 :(得分:2)

合并两个大小为k的已排序数组(或列表)。 m最多只进行k+m-1次比较min{k,m}。 (在每次比较之后,我们可以向目标写一个值,当两个中的一个用尽时,不再需要进行比较。)

C(n)成为n元素的数组(列表)的合并出口的最差情况比较。

然后我们很明显地C(1) = 0C(2) = 1。此外,我们有复发

C(n) = C(floor(n/2)) + C(ceiling(n/2)) + (n-1)

简单的归纳显示

C(n) <= n*log_2 n

另一方面,很容易看出我们可以任意接近边界(对于每个ε > 0,我们可以构造需要超过(1-ε)*n*log_2 n比较的情况),因此mergesort的常量是1。

答案 3 :(得分:0)

合并排序为O(n log n),并且在每个步骤中,在“最差”情况下(对于比较次数),执行比较。

另一方面,Quicksort在最坏的情况下是O(n ^ 2)。

答案 4 :(得分:0)

计算归并排序中比较次数的C++程序。 程序首先对给定的数组进行排序,然后显示比较的次数。

 #include<iostream>
 using namespace std;
 int  count=0; /* to count the number of comparisions */

 int merge( int arr [ ], int l, int m, int r)
{
 int i=l; /* left subarray*/
 int j=m+1; /* right  subarray*/
 int k=l; /* temporary array*/
 int temp[r+1];
 while( i<=m && j<=r)
 {
   if ( arr[i]<= arr[j])
  {
    temp[k]=arr[i];
    i++;
  }
   else
  {
    temp[k]=arr[j];
    j++;
  }
    k++;
    count++;

  }
   while( i<=m)
  {
    temp[k]=arr[i];
    i++;
    k++;
  }
    while( j<=r)
  {
    temp[k]=arr[j];
    j++;
    k++;
  }
  for( int p=l; p<=r; p++)
  {
    arr[p]=temp[p];
  }
   return count;
  }


  int  mergesort( int arr[ ], int l, int r)
  {
    int comparisons;
    if(l<r)
  {
   int m= ( l+r)/2;
   mergesort(arr,l,m);
   mergesort(arr,m+1,r);
   comparisions = merge(arr,l,m,r);
  }
   return comparisons;
  }

 int main ()
 {
   int size;
   cout<<" Enter the size of an array "<< endl;
   cin>>size;
   int myarr[size];
   cout<<"  Enter the elements of array "<<endl;
   for ( int i=0; i< size; i++)
 {
   cin>>myarr[i];
 }
 cout<<"  Elements of array before sorting are  "<<endl;
 for ( int i=0; i< size; i++)
 {
   cout<<myarr[i]<<"  " ;
 }
  cout<<endl;
  int c=mergesort(myarr, 0, size-1);
  cout<<"  Elements of array after sorting are  "<<endl;
  for ( int i=0; i< size; i++)
 {
   cout<<myarr[i]<<"  " ;
 }
   cout<<endl;
   cout<<"  Number of comaprisions while sorting the given array"<< c <<endl;
   return 0;
 }

答案 5 :(得分:0)

我假设读者知道合并排序。只有当两个排序的数组合并时才会进行比较。为简单起见,假设 n 为 2 的幂。要在最坏情况下合并两个 (n/2) 大小的数组,我们需要 (n - 1) 次比较。 -1 出现在这里,因为合并时剩下的最后一个元素不需要任何比较。首先找到总比较数假设它为n,我们可以通过(-1)部分对其进行修正。合并的级别数是 log2(n)(想象为树结构)。在每一层中都会有 n 个比较(需要减去一些数字,因为 -1 部分),所以总比较是 nlog2(n) -(尚未找到)。 “尚未发现”部分没有给出 nlog2(n) 常数,它实际上是 (1 + 2 + 4 + 8 + ... + (n/2) = n - 1)。 合并排序中的总比较数 = n*log2(n) - (n - 1)。 所以,你的常数是 1。