歸併排序演算法詳解及其優化
阿新 • • 發佈:2018-12-23
歸併排序演算法:
思想:分治法
每個遞迴過程涉及三個步驟
第一, 分解: 把待排序的 n 個元素的序列分解成兩個子序列, 每個子序列包括 n/2 個元素.
第二, 治理: 對每個子序列分別呼叫歸併排序__MergeSort, 進行遞迴操作
第三, 合併: 合併兩個排好序的子序列,生成排序結果.
void __MergeSort( int *a, int left, int right, int * tmp )
{
if( left >= right ) //退出條件
return;
int mid = left+((right-left)>>1 );
__MergeSort(a,left,mid,tmp); // 遞迴左半陣列
__MergeSort(a,mid+1,right,tmp); // 遞迴右半陣列
//將排好序的兩部分陣列歸併(排序)
int begin1 = left,end1 = mid;
int begin2 = mid+1,end2 = right;
int index = left;
while( begin1<=end1 && begin2<=end2 )// 迴圈條件:任一個陣列排序完,則終止條件,最後將沒有比較完的陣列直接一一拷過去
{
if ( a[begin1] <= a[begin2] )
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while( begin1 <= end1 )//右半陣列走完了
{
tmp[index++] = a[begin1++];
}
while( begin2 <= end2 )//左半陣列走完了
{
tmp[index ++] = a[begin2++];
}
//tmp陣列已經排好序,將陣列內容拷到原陣列,遞歸向上一層走
index = left;
while( index <= right )
{
a[index] = tmp[index];
++index;
}
}
void MergeSort( int *a,size_t n )
{
int *tmp = new int[n]; // 開一個第三方陣列來存取左右排好序歸併後的序列
__MergeSort(a,0,n-1,tmp);
delete[] tmp; // 最後釋放第三方空間
}
優化:
在遞迴子問題的時候在區間內的資料比較少的時候我們可以不再劃分區間,直接用直接插入排序效率會更高,因為接著劃分又要建立棧楨,沒有必要
void __MergeSort( int *a, int left, int right, int * tmp )
{
if( left >= right ) //退出條件
return;
if( right-left+1 <10 )//優化
{
InsertSort(a+left,right-left+1);
}
int mid = left+((right-left)>>1);
__MergeSort(a,left,mid,tmp); // 遞迴左半陣列
__MergeSort(a,mid+1,right,tmp); // 遞迴右半陣列
//將排好序的兩部分陣列歸併(排序)
int begin1 = left,end1 = mid;
int begin2 = mid+1,end2 = right;
int index = left;
while( begin1<=end1 && begin2<=end2 )// 迴圈條件:任一個陣列排序完,則終止條件,最後將沒有比較完的陣列直接一一拷過去
{
if( a[begin1] <= a[begin2] )
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while( begin1 <= end1 )//右半陣列走完了
{
tmp[index++] = a[begin1++];
}
while( begin2 <= end2 )//左半陣列走完了
{
tmp[index++] = a[begin2++];
}
//tmp陣列已經排好序,將陣列內容拷到原陣列,遞歸向上一層走
index = left;
while( index <= right )
{
a[index] = tmp[index];
++index;
}
}
void MergeSort( int *a,size_t n )
{
int *tmp = new int[n]; // 開一個第三方陣列來存取左右排好序歸併後的序列
__MergeSort(a,0,n-1,tmp);
delete[] tmp; // 最後釋放第三方空間
}
完整程式碼:
#include <iostream>
using namespace std;
#include <assert.h>
//直接插入排序
void InsertSort (int* a,size_t n)
{
assert(a);
for(size_t i = 1;i < n; ++i)//用end的位置控制邊界
{
//單趟排序
int end = i - 1 ;
int tmp = a[i];
while( end >= 0 )//迴圈繼續條件
{
if( a[end] > tmp )
{
a[end+1] = a[end];
--end;
}
else
break;
}
a[end+1] = tmp;
}
}
void __MergeSort( int *a, int left, int right, int * tmp )
{
if( left >= right ) //退出條件
return;
if( right-left+1 <10 )//優化
{
InsertSort(a+left,right-left+1);
}
int mid = left+((right-left)>>1);
__MergeSort(a,left,mid,tmp); // 遞迴左半陣列
__MergeSort(a,mid+1,right,tmp); // 遞迴右半陣列
//將排好序的兩部分陣列歸併(排序)
int begin1 = left,end1 = mid;
int begin2 = mid+1,end2 = right;
int index = left;
while( begin1<=end1 && begin2<=end2 )// 迴圈條件:任一個陣列排序完,則終止條件,最後將沒有比較完的陣列直接一一拷過去
{
if( a[begin1] <= a[begin2] )
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while( begin1 <= end1 )//右半陣列走完了
{
tmp[index++] = a[begin1++];
}
while( begin2 <= end2 )//左半陣列走完了
{
tmp[index++] = a[begin2++];
}
//tmp陣列已經排好序,將陣列內容拷到原陣列,遞歸向上一層走
index = left;
while( index <= right )
{
a[index] = tmp[index];
++index;
}
}
// 歸併排序
void MergeSort( int *a,size_t n )
{
int *tmp = new int[n]; // 開一個第三方陣列來存取左右排好序歸併後的序列
__MergeSort(a,0,n-1,tmp);
delete[] tmp; // 最後釋放第三方空間
}
void Print(int a[],int len)
{
cout<<endl;
for(int i = 0; i < len; ++i)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void test()
{
//升序排序
int a[] = {9,8,7,6,5,4,3,2,1};
/* int a[] = {2,5,4,0,9,3,6,8,7,1};*/
int len = sizeof(a)/sizeof(a[0]);
cout<<"before sort :";
Print(a,len);
MergeSort(a,len);
cout<<"after sort :";
Print(a,len);
}
int main ()
{
test();
return 0;
}