1. 程式人生 > >二路歸併排序(未完)

二路歸併排序(未完)

注:歸併演算法是典型的分治法例項,即將一個大問題分解為n個原子問題(不可再分割),將這n個小問題解決後再逐漸合併,一般使用的是遞迴法。歸併排序是一種交穩定的排序演算法,時間複雜度固定式nlogn,但在實際操作中,其時間複雜度將會遠遠大於該值,因為在程式碼執行過程中不停的申請臨時記憶體和釋放記憶體,

歸併排序的優化

歸併排序的圖解

遞迴實現二路歸併排序

#include<iostream>
#include<vector>
using namespace std;
void merge(int arr[],int min,int m,int max){ 
int *tem=new int [max-min+1];
int k=0,i=min,j=m+1;
while(i<=m&&j<=max){
if(arr[i]>arr[j])
	tem[k++]=arr[j++];
else
	tem[k++]=arr[i++];	
} 
while(i<=m)tem[k++]=arr[i++];
while(j<=max)tem[k++]=arr[j++];
//for(int l=0;l<=max-min;l++)
//arr[l]=tem[l];//此處傳遞值是錯誤的,因為在一次迴圈中完成的是min到max的排序,
//只要把該區間的數值傳遞即可
for(int l=min,p=0;l<=max;p++,l++)
arr[l]=tem[p];
for(int ll=0;ll<10;ll++)//改程式碼可以清晰的看見排序過程,每次執行該函式的時候,
//就是對min到max的排序,而且在排序結束之後改變了原arr陣列的值
cout<<arr[ll];
cout<<endl<<"-----------------"<<endl;
}
void Msort(int arr[],int min,int max){
	if(min>=max)
	return;
	else{
		Msort(arr,min,(min+max)/2);
		Msort(arr,(max+min)/2+1,max);
		merge(arr,min,(min+max)/2,max);
	}
}
int main(){
int arr[]={0,7,2,5,1,3,4,6,9,8};
Msort(arr,0,9);
for(int i=0;i<10;i++)
cout<<arr[i];
return 0;
} 

分析:如果對Merge的每個遞迴呼叫均區域性宣告一個臨時陣列,那麼在任意時刻就可能有logn個數組處在活動期,對記憶體是一個極大的消耗。另外在分配和收回上也會消耗很多時間。

時間複雜度的推算很複雜,涉及到疊縮求和,我認為在Msort函式中呼叫了logn次merge函式,在merge函式的排序的時間複雜度是N所以整個的時間複雜度是nlogn


另外二路歸併排序的迭代做法有效的防止了棧溢位,但是程式碼不夠簡潔,等我摸清楚後再更新

迭代實現二路遞迴排序

#include<iostream>
#include<algorithm>
using namespace std;
void merge(int arr[],int min,int m,int max){ 
int *tem=new int [max-min+1];
int k=0,i=min,j=m+1;
while(i<=m&&j<=max){
if(arr[i]>arr[j])
	tem[k++]=arr[j++];
else
	tem[k++]=arr[i++];	
} 
while(i<=m)tem[k++]=arr[i++];
while(j<=max)tem[k++]=arr[j++];
for(int l=min,p=0;l<=max;p++,l++)
arr[l]=tem[p];
for(int ll=0;ll<10;ll++)
cout<<arr[ll];
cout<<endl<<"-----------------"<<endl;
}
void Msort(int arr[],int n){
	for(int step=2;step/2<=n;step*=2){//step表示步長,至於為什麼是2的次冪,
	//我想應該是對應logn,
		for(int i=0;i<=n;i+=step){
				merge(arr,i,i+step/2-1,min(n-1,i+step-1));	
		}
	} 
}
int main(){
int arr[]={9,8,7,6,5,4,3,2,1,0};
Msort(arr,10);
for(int i=0;i<10;i++)
cout<<arr[i];
return 0;
}

分析:其實遞迴和非遞迴的merge函式基本沒什麼變化,主要思想就是怎麼將目標序列拆分,並按傳入merge函式,merge(arr,i,i+step/2-1,min(n-1,i+step-1));中的i+step/2-1表示傳入區間的中點,注意,該中點並非(min+max)/2,比如在區間為0 1 2 3 4 5 6 7 8 9 中,當步長為8時,前後區間的個數分別為8和2.

本來想使用vector陣列,這樣省去了動態申請陣列的過程,無奈道行太淺,怎麼都除錯不出來。稍後再試試。