1. 程式人生 > >歸併排序中對小陣列採用插入排序

歸併排序中對小陣列採用插入排序

    儘管合併排序最壞情況執行時間為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();

}