尽管合并排序最坏情况运行时间为O(nlgn),插入排序的最坏运行时间为O(n^2),但是插入排序的常数因子使得它在n较小时,运行要更快一些。因此,在合并排序算法中,当子问题足够小时,采用插入排序就比较合适了。考虑对合并排序做这样的修改,即采用插入排序策略,对n/k 个长度为 k 的子列表进行排序。然后,再用标准的合并机制将它们合并起来,此处k是一个待定的值。
注:书里的那个O里还有一杠的符号打不出来,所以这里用大O代替了,特指同阶无穷大量。
a) 证明在最坏的情况下,n/k个子列表可以用插入排序在O(nk)的时间内完成排序
b) 证明这些子列表可以在O(nlg(n/k))最坏情况内完成合并。
c) 如果已知修改后的合并排序算法的最坏运行时间为O(nk+nlg(n/k)),要使得修改后的算法具有与标准合并算法一样的渐进运行时间,k的最大渐进值( 即O形式)是什么(以n的函数形式表示)?
d) 实践中,应该如何选取k值。
最佳答案
第一个O(n/k*k^2)=O(nk).这里的O中间有一横
第二个n/k个列表两两合并,合并完继续合并,共lg(n/k)对,合并的代价O(n).所以O(nlg(n/k)).
第三个O(nk+nlg(n/k))=O(nlgn).只能最大是k=O(lgn).等式左边中第一项是高阶项。k如果大于lgn,则比归并排序复杂度大了。左边可以写成nk+nlgn-nlgk,k等于lgn时,就是2nlgn-nlglgn.与归并排序是一样的。
第四个k应当是最大的列表长度,这个长度上插入排序要比归并排序快。
#include<iostream>
#include<ctime>
#include <limits>
#include <cmath>
#include <boost/timer/timer.hpp>
using namespace boost::timer;
using namespace std;
void merge(int *a,int p,int q,int r)//归并哨兵法
{
int Ln=q-p+1;
int Rn=r-q;
int *La=new int[Ln+1];
int *Ra=new int[Rn+1];
for(int i=0;i<Ln;++i){
La[i]=a[i+p];
}
La[Ln]=numeric_limits<int>::max();
for(int j=0;j<Rn;++j){
Ra[j]=a[q+j+1];
}
Ra[Rn]=numeric_limits<int>::max();
int k=0,m=0;
for(int n=p;n<=r;++n){
if(La[k]<Ra[m]){
a[n]=La[k];
++k;
}else{
a[n]=Ra[m];
m++;
}
}
delete [] La;
delete [] Ra;
}
void insert_sort(int *a ,int n)//直接插入法
{
int key,j;
for(int i=1;i<n;++i){
key=a[i];
j=i-1;
while(j>=0 && key<a[j]){
a[j+1]=a[j];
--j;
}
a[j+1]=key;
}
}
void merge_insertion_sort(int *a,int p,int q,int k)//归并,直接插入联合使用
{
if(p<q){
if((q-p+1)>k){
int mid=(p+q)>>1;
merge_insertion_sort(a,p,mid,k);
merge_insertion_sort(a,mid+1,q,k);
merge(a,p,mid,q);
}else{
insert_sort(a+p,q-p+1);
}
}
}
void merge_sort(int *a,int p,int q)
{
if(p<q){
int mid=(p+q)>>1;
merge_sort(a,p,mid);
merge_sort(a,mid+1,q);
//merge(a,p,mid,q);
merge(a,p,mid,q);
}
}
int main()
{
srand(time(NULL));
int n;
//while((n= rand()%100)<3);
n=500000;
int * a=new int[n];
int * b=new int[n];
for(int i=0;i<n;i++){
a[i]=rand()%100000;
b[i]=a[i];
}
cpu_timer t;
merge_insertion_sort(a,0,n-1,log(n)/log(2));
t.stop();
cpu_timer t2;
merge_sort(b,0,n-1);
t2.stop();
delete [] a;
delete [] b;
cout<<t.format();
cout<<t2.format();
}