我已经读过,quicksort在实践中比mergesort快得多,其原因是隐藏的常量。 那么,随机快速排序复杂度的解决方案是2nlnn = 1.39nlogn,这意味着快速排序中的常数是1.39。 但是mergesort怎么样? mergesort中的常量是什么?
答案 0 :(得分:17)
让我们看看我们是否可以解决这个问题!
在合并排序中,在递归的每个级别,我们执行以下操作:
那么每一步都进行了多少次比较?那么,除法步骤不进行任何比较;它只是将数组分成两半。第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
令人印象深刻的是,这比
这个分析比最优分析精确得有点精确,但Wikipedia证实分析大致为n lg n,这确实比quicksort的平均情况下的比较少。
希望这有帮助!
答案 1 :(得分:3)
在最糟糕的情况下,假设直接实现,对 n 元素进行排序的比较次数为
其中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) = 0
,C(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。