基本排序之詳解歸併排序

                                 

                                                                       歸併排序


一、歸併排序的效率是僅次於快速排序的穩定的排序算法,其時間複雜度爲O(nlog2n)。我們對n 個元素進行一次歸併排序時,歸併的次數約爲log2n,每一次的兩路歸併排序元素的比較次數約爲n-1。


二、歸併排序的基本思想:

        歸併排序是通過將一列數組序列中的元素看成一個一個的小序列來進行歸併,直到所有的元素都被歸併完成後,排序即完成,便是成功的完成了排序操作。


三、原理:

        如:我們對n 爲9吧,這樣更加好,如,a[9] = {1,2,5,4,3,8,6,7,9}這樣的一個數組:

         原數組:

                      1       2      5     4      3      8     6    7     9

         第一次歸併:

                     [1    2]    [4    5]    [3    8]    [6     7]     [9]

         第二次歸併:

                     [1    2      4     5]      [3    6     7     8]     [9]

         第三次歸併:

                   [1    2     3       4      5     6      7    8]     [9]   

         第四次歸併:

                    [1    2     3       4      5     6      7    8      9]  

實現歸併的操作代碼:

			     for(i = L1,j = L2;i<=u1&&j <= u2;){ 
			         if(a[i] <= a[j]){
					        A->swap[pos] = a[i];
							i++;
						    pos++;
					   }else{
						    A->swap[pos] =  a[j];
						    j++;
						    pos++;				   
					   }
			     }
				
			   //此時我們要做的已經歸併完成了
			   //此時我們要對在數組序列沒有歸併的進行歸併
			   while(i <= u1 ){
			         A->swap[pos]=a[i];
					 i++;
					 pos++;
			   }
			   while(j <= u2 ){
			          A->swap[pos] = a[j];
					  j++;
					  pos++;
			   }
			   //此時對兩個數組序列已經徹底歸併完成了,而且此時是有序序列
			   //此時的L1 = u2+1了
			   L1 = u2 +1;
		//這裏我們把只有一組的也存放到swap中
		for(int i = L1;i<length1;i++){
		       A->swap[i] = a[i];
		}


實現一次執行歸併的核心代碼:

/**
*歸併排序的一次執行
*@param   int a[] 爲接收的數組
*@param   int lenghth1 爲此接收數組的長度
*@param   int swap[], 爲保存執行完畢一次歸併排序後的數組
*@param   int length2 爲此歸併好了的數組序列的長度
*@return  無
*/
void Merge(int a[],int length1 ,Array *A,int length2){
	    
	   //接收了數組我們需要對其進行歸併和一次排序這時我們定義兩個有指向的“指針”
	    int u1,u2;//此爲這兩個便於掃描的指針
	   //此時又需要定義兩個數組序列的下界
		int L1,L2;
		//我們可以知道第一個數組序列的下界爲0
		L1 = 0;
		//此時設置swap中的下標值pos來保存歸併成功的數,因爲pos是針對這整個循環的所以只能放在循環外面
	    int pos = 0;
		while(L1+length2<length1){
		       //我們可以得到第二個數組序列的下界了
		       L2 = L1 +length2;
	           //此時讓u1,u2指向數組序列的上界
		       u1 =L2 -1;
	          //這時我們需要判斷第二個數組序列是否越界,因爲這時會出現最後一個數組序列不滿length2這個值如[1 2 3  4][6]
		       if(L2 +length2-1< length1-1){
		               u2 = L2 +length2-1;
		       }else{
		               u2 = length1-1;
		       }
		     //現在我們把所有的指向問題都解決了,現在就需要進行掃描歸併操作了
		     //此時對兩個數組序列進行歸併排序
		       int i,j;
			   i = L1,j = L2;
			   /**
			     *這裏是方便的操作
			     *for(i = L1,j = L2;i<=u1&&j <= u2;){ 
			     *   if(a[i] <= a[j]){
				 *	        A->swap[pos] = a[i];
				 *			i++;
				 *		    pos++;
				 *	   }else{
				 *		    A->swap[pos] =  a[j];
				 *		    j++;
				 *		    pos++;				   
				 *	   }
				 * }
				 */
			   for(;;){
				   if(i<=u1&&j <= u2){
					   if(a[i] <= a[j]){
					        A->swap[pos] = a[i];
							i++;
							pos++;
					   }else{
						    A->swap[pos] =  a[j];
						    j++;
						    pos++;				   
					   }
				   }else{
				        break;
				   }
			   }
			   //此時我們要做的已經歸併完成了
			   //此時我們要對在數組序列沒有歸併的進行歸併
			   while(i <= u1 ){
			         A->swap[pos]=a[i];
					 i++;
					 pos++;
			   }
			   while(j <= u2 ){
			          A->swap[pos] = a[j];
					  j++;
					  pos++;
			   }
			   //此時對兩個數組序列已經徹底歸併完成了,而且此時是有序序列
			   //此時的L1 = u2+1了
			   L1 = u2 +1;
	    }

		//cout<<"L1"<<L1<<endl;
		//cout<<"lenght1"<<length1<<endl;
	    /*for(int i = 0;i <length1 ;i++){
		      cout<<"A->swap[i]......i="<<i<<"  "<<A->swap[i]<<endl;
		}*/
		//這裏我們把只有一組的也存放到swap中
		for(int i = L1;i<length1;i++){
		       A->swap[i] = a[i];
		}
		//這樣一次歸併操作徹底完成了
		/*for(int i = 0;i <length1 ;i++){
		      cout<<"A->swap[i]......i="<<i<<"  "<<A->swap[i]<<endl;
		}
		cout<<"count....."<<count<<endl;*/
}
實現歸併排序的代碼段:

/**
*歸併排序
*@param int a[]帶歸併的數組序列
*@param int length1 爲帶歸併的數組序列的長度
*@return 無
*/
void Merge_sort(int a[],int length1){
	  //我們知道我們以數組長度爲1開始歸併
	  int length2 = 1;
	  //這裏我們需要申請一個swap空間來作爲交換的空間
	  Array *A =NULL;
	  if(A != NULL){
		  free(A);
	  }
	  A = (Array *)malloc(sizeof(Array )*length1);
	  if(A != NULL){
	       cout<<"申請空間成功!"<<endl;
	  }
	  while(length2 <length1){//直到歸併完成Across跳出循環
	       Merge(a,length1,A,length2); 
		   for(int i = 0;i <length1;i++){
		         a[i]  = A->swap[i];//這裏把歸併後的數組序列再次保存回a[i]中
		   }
		   //這裏需要更改歸併後的新數組序列的長度
		   length2 = 2*length2;
	  }
	  //記住最後一定要對空間進行釋放
	  free(A);
}
全部代碼:

/**
*歸併排序就是將一個數組分成若干個數組然後兩兩合併直到合併完成最後一個數組
*@author 菜鳥
*@version 2014.6.15
*/
#include <iostream>
#include <malloc.h>
#include <windows.h>
#define maxSize  100
using namespace std;
typedef int DataType;
typedef struct{
     DataType swap[maxSize];
}Array;
/**
*歸併排序的一次執行
*@param   int a[] 爲接收的數組
*@param   int lenghth1 爲此接收數組的長度
*@param   int swap[], 爲保存執行完畢一次歸併排序後的數組
*@param   int length2 爲此歸併好了的數組序列的長度
*@return  無
*/
void Merge(int a[],int length1 ,Array *A,int length2){
	    
	   //接收了數組我們需要對其進行歸併和一次排序這時我們定義兩個有指向的“指針”
	    int u1,u2;//此爲這兩個便於掃描的指針
	   //此時又需要定義兩個數組序列的下界
		int L1,L2;
		//我們可以知道第一個數組序列的下界爲0
		L1 = 0;
		//此時設置swap中的下標值pos來保存歸併成功的數,因爲pos是針對這整個循環的所以只能放在循環外面
	    int pos = 0;
		while(L1+length2<length1){
		       //我們可以得到第二個數組序列的下界了
		       L2 = L1 +length2;
	           //此時讓u1,u2指向數組序列的上界
		       u1 =L2 -1;
	          //這時我們需要判斷第二個數組序列是否越界,因爲這時會出現最後一個數組序列不滿length2這個值如[1 2 3  4][6]
		       if(L2 +length2-1< length1-1){
		               u2 = L2 +length2-1;
		       }else{
		               u2 = length1-1;
		       }
		     //現在我們把所有的指向問題都解決了,現在就需要進行掃描歸併操作了
		     //此時對兩個數組序列進行歸併排序
		       int i,j;
			   i = L1,j = L2;
			   /**
			     *這裏是方便的操作
			     *for(i = L1,j = L2;i<=u1&&j <= u2;){ 
			     *   if(a[i] <= a[j]){
				 *	        A->swap[pos] = a[i];
				 *			i++;
				 *		    pos++;
				 *	   }else{
				 *		    A->swap[pos] =  a[j];
				 *		    j++;
				 *		    pos++;				   
				 *	   }
				 * }
				 */
			   for(;;){
				   if(i<=u1&&j <= u2){
					   if(a[i] <= a[j]){
					        A->swap[pos] = a[i];
							i++;
							pos++;
					   }else{
						    A->swap[pos] =  a[j];
						    j++;
						    pos++;				   
					   }
				   }else{
				        break;
				   }
			   }
			   //此時我們要做的已經歸併完成了
			   //此時我們要對在數組序列沒有歸併的進行歸併
			   while(i <= u1 ){
			         A->swap[pos]=a[i];
					 i++;
					 pos++;
			   }
			   while(j <= u2 ){
			          A->swap[pos] = a[j];
					  j++;
					  pos++;
			   }
			   //此時對兩個數組序列已經徹底歸併完成了,而且此時是有序序列
			   //此時的L1 = u2+1了
			   L1 = u2 +1;
	    }


		//cout<<"L1"<<L1<<endl;
		//cout<<"lenght1"<<length1<<endl;
	    /*for(int i = 0;i <length1 ;i++){
		      cout<<"A->swap[i]......i="<<i<<"  "<<A->swap[i]<<endl;
		}*/
		//這裏我們把只有一組的也存放到swap中
		for(int i = L1;i<length1;i++){
		       A->swap[i] = a[i];
		}
		//這樣一次歸併操作徹底完成了
		/*for(int i = 0;i <length1 ;i++){
		      cout<<"A->swap[i]......i="<<i<<"  "<<A->swap[i]<<endl;
		}
		cout<<"count....."<<count<<endl;*/
}
/**
*歸併排序
*@param int a[]帶歸併的數組序列
*@param int length1 爲帶歸併的數組序列的長度
*@return 無
*/
void Merge_sort(int a[],int length1){
	  //我們知道我們以數組長度爲1開始歸併
	  int length2 = 1;
	  //這裏我們需要申請一個swap空間來作爲交換的空間
	  Array *A =NULL;
	  if(A != NULL){
		  free(A);
	  }
	  A = (Array *)malloc(sizeof(Array )*length1);
	  if(A != NULL){
	       cout<<"申請空間成功!"<<endl;
	  }
	  while(length2 <length1){//直到歸併完成Across跳出循環
	       Merge(a,length1,A,length2); 
		   for(int i = 0;i <length1;i++){
		         a[i]  = A->swap[i];//這裏把歸併後的數組序列再次保存回a[i]中
		   }
		   //這裏需要更改歸併後的新數組序列的長度
		   length2 = 2*length2;
	  }
	  //記住最後一定要對空間進行釋放
	  free(A);
}
/**
*這裏需要一個輸出函數,對數組序列進行輸出out_put()
*@param int a[] 表示接受此數組的地址
*@param int length 表示此數組的長度
*@return 無
*/


void out_put(int a[],int length){
	for(int i = 0; i< length ;i++){
	        cout<<"第"<<i+1<<"個元素:"<<a[i]<<endl;
	}
	cout<<"輸出完成!"<<endl;
}


int main(){
	  int a[10] = {1,2,5,4,3,8,6,7,9,0};
	  cout<<"未經排序的序列:"<<endl;
	  out_put(a,10);
	  Merge_sort(a,10);
	  cout<<"經歸併排序後的序列:"<<endl;
	  out_put(a,10);
          system("PAUSE");
	  return 1;


}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章