归并排序也是一种很有名的排序算法,分而治之的思想。但与同样是分治的快排相比,归并更侧重于“合”,“分”其实是很简单的。
比较重要的一点是归并排序的递归出口问题,当下标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;
}
欢迎留言讨论对该算法进行优化。