歸併排序也是一種很有名的排序算法,分而治之的思想。但與同樣是分治的快排相比,歸併更側重於“合”,“分”其實是很簡單的。
比較重要的一點是歸併排序的遞歸出口問題,當下標left>=right時就不需要再遞歸了。
歸併排序的不太好的一點是空間複雜度有點兒高。前一段百度實習生招聘,有一個算法設計就是對merge函數進行優化,把空間複雜度降爲O(1),有好的處理方式請留言。
最常規的實現方式:
#include <iostream>
#include <assert.h>
#define N 20
using namespace std;
//輸出數組內容
template <class T>
void print(T arr[],int size)
{
for(int i=0;i<size;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
//歸併排序(遞歸實現)
template <class T>
void mergeSort(T arr[],T temp[],int left,int right)
{
//l爲數組最左側下標,r爲最右側下標,m爲中間位置下標
int l=left,r=right,m=(left+right)/2;
//遞歸出口
if(l>=r)
{
return;
}
//對左字串和右字串分別進行遞歸排序
mergeSort(arr,temp,l,m);
mergeSort(arr,temp,m+1,r);
//對兩個字串進行歸併
merge(arr,temp,l,m,r);
}
//歸併函數
template <class T>
void merge(T arr[],T temp[],int left,int middle,int right)
{
//下標left到middle是左字串,下標middle+1到right是右字串
int l=left,m=middle,r=right;
//注意別犯i=0,j=0等的腦殘錯誤,第一次寫的時候竟然這樣寫了
//這個歸併函數需要多次調用,所以下標未必是從0開始
int i=l,j=m+1,k=l;
//比較兩個字串的元素值,把較小的放進temp數組中
while(i<=m && j<=r)
{
//if(arr[i]<=arr[j])
//{
// temp[k++]=arr[i++];
//}
//else
//{
// temp[k++]=arr[j++];
//}
//使用三元運算符的簡潔寫法
temp[k++]=((arr[i]<arr[j])?arr[i++]:arr[j++]);
}
//if(i>m)
//{
// while(j<=r)
// {
// temp[k++]=arr[j++];
// }
//}
//else
//{
// while(i<=m)
// {
// temp[k++]=arr[i++];
// }
//}
//其實無需判定i和m的關係,使用兩個while循環即可(儘管只會執行一個)
while(j<=r)
{
temp[k++]=arr[j++];
}
while(i<=m)
{
temp[k++]=arr[i++];
}
//最後把temp數組值賦給arr數組
for(i=l;i<=r;i++)
{
arr[i]=temp[i];
}
}
int main()
{
//int arr[N]={5,4,10,3,9};
//int arr[N]={5,4,0,3,9,22,7,22,0,-10};
int arr[N]={5,4,1,3,9,12,7,22,0,-10,12,33,-22,0,88,123,-45,345,-98,-666};
int temp[N]; //額外開闢的數組
cout<<"排序前:"<<endl;
print(arr,N);
mergeSort(arr,temp,0,N-1);
cout<<"排序後:"<<endl;
print(arr,N);
return 0;
}
歡迎留言討論對該算法進行優化。