归并排序中对小数组采用插入排序

    尽管合并排序最坏情况运行时间为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)).

         如果仅仅由两个字表合并,则每个数据被查找且移动一次,而n/k个字表时,每个数组查找且移动lg(n/k)次,

           故为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();

}




  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值