Algorithm 合并排序究竟进行了多少次比较?
我已经读到,在实践中,快速排序比合并排序快得多,其原因是隐藏常量 随机快速排序复杂性的解决方案是2nlnn=1.39nlogn,这意味着快速排序中的常数是1.39 但是合并排序呢?合并排序中的常量是什么?合并两个大小分别为Algorithm 合并排序究竟进行了多少次比较?,algorithm,sorting,complexity-theory,quicksort,mergesort,Algorithm,Sorting,Complexity Theory,Quicksort,Mergesort,我已经读到,在实践中,快速排序比合并排序快得多,其原因是隐藏常量 随机快速排序复杂性的解决方案是2nlnn=1.39nlogn,这意味着快速排序中的常数是1.39 但是合并排序呢?合并排序中的常量是什么?合并两个大小分别为kresp的排序数组(或列表)m最多进行k+m-1比较,最多进行min{k,m}。(每次比较后,我们可以向目标写入一个值,当两个值中的一个用完时,就不需要再进行比较了。) 设C(n)为n元素数组(列表)的合并排序的最坏比较数 很明显,我们有C(1)=0,C(2)=1。此外,我们
k
resp的排序数组(或列表)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
C(n)让我们看看能否解决这个问题
在合并排序中,我们在递归的每个级别执行以下操作:
将阵列一分为二
对每一半进行递归排序
使用合并算法将两半合并在一起
那么,每一步要做多少比较呢?好吧,分割步骤不会进行任何比较;它只是将阵列一分为二。步骤2没有(直接)进行任何比较;所有比较都是通过递归调用完成的。在步骤3中,我们有两个大小为n/2的数组,需要合并它们。这最多需要n个比较,因为合并算法的每个步骤都会进行一次比较,然后消耗一些数组元素,所以我们只能进行n个比较
#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;
}
结合这一点,我们得到以下结果:
C(1) = 0
C(n) = 2C(n / 2) + n
(如评论中所述,线性项更精确地表示为(n-1),尽管这不会改变总体结论。我们将使用上述递归作为上限。)
为了简化这个过程,让我们定义n=2k并用k重写这个循环:
C'(0) = 0
C'(k) = 2C'(k - 1) + 2^k
这里的前几个术语是0,2,8,24。这看起来像k2k,我们可以用归纳法证明。作为我们的基本情况,当k=0时,第一项为0,k2k的值也为0。对于归纳步骤,假设索赔适用于一些k,并考虑K+ 1。那么这个值是2(k2k)+2k+1=k2k+1+2k+1=(k+1)2k+1,所以这个声明对k+1有效,完成归纳。因此,C’(k)的值是k2k。因为n=2k,这意味着,假设n是2的完美幂,我们得到的比较次数是
C(n)=n lg n
令人印象深刻的是,这比快速排序更好!那么,为什么快速排序比合并排序快呢?这与其他因素有关,而这些因素与所做比较的数量无关。基本上,由于快速排序在适当的位置工作,而合并排序在适当的位置工作,所以在合并排序中引用的位置不如在快速排序中好。这是一个巨大的因素,在实践中,快速排序比合并排序要好得多,因为缓存丢失的成本非常巨大。此外,排序数组所需的时间不仅仅考虑比较的数量。其他因素(如每个数组元素移动的次数)也很重要。例如,在合并排序中,我们需要为缓冲元素分配空间,移动元素以便可以合并它们,然后合并回数组。在我们的分析中,这些动作并不算在内,但它们肯定是合二为一的。将此与quicksort的分区步骤进行比较,quicksort的分区步骤只移动每个数组元素一次,并保持在原始数组中。这些额外的因素,而不是所做比较的数量,支配着算法的运行时间
该分析比最佳分析的精度稍低,但证实了该分析大致为n lg n,并且与quicksort的平均情况相比,这确实是较少的比较
希望这有帮助 合并排序为O(n logn),在每个步骤中,在“最坏”情况下(对于比较次数),执行比较
另一方面,快速排序在最坏的情况下是O(n^2)。在最坏的情况下,假设采用直接实现,则对n个元素进行排序的比较次数为
N⌈lg n⌉ − 2.⌈lg n⌉ + 1.
其中lg n表示n的值
这个结果可以在Donald Knuth的或最新版本的中找到,我刚刚写下了一个证明。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;
}
#包括
使用名称空间std;
整数计数=0;/*计算比较的次数*/
整数合并(整数arr[],整数l,整数m,整数r)
{
int i=l;/*左子阵列*/
int j=m+1;/*右子阵列*/
int k=l;/*临时数组*/
内部温度[r+1];
然而,如果没有更多的细节,这个问题没有答案。答案取决于(1)你对复杂性的定义:操作次数?比较次数?(2)不同机器的答案可能不同,这取决于每台机器的指令集。当然是比较的次数。这意味着我的1.39快速排序常量不正确。@geniaz1-你的快速排序常量确实正确,但由于其他原因,快速排序更快。有关详细信息,请参阅我的帖子。非常感谢!我有什么分析可以将空间分配计算在内吗?公式不应该是C(1)=0 C(n)=2C(n/2)+n-1。因为如果我们有两个大小为n/2的数组,我们最多需要n-1个比较来将它们合并到一个大小为n的数组中吗?@Johnson是的!这是一个很好的观点。这将使整体分析减少2n-1(每个递归调用一个),我相信这不会改变结论。谢谢你这么做!合并中的比较次数不应该是(n-1)吗?有没有快速排序的想法?