归并排序(MergeSort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
归并排序,它采取分而治之(Divide-and-Conquer)的策略,时间复杂度是O(N*lgN)。
归并排序的步骤如下:
1. Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。
2. Conquer: 对这两个子序列分别采用归并排序。
3. Combine: 将两个排序好的子序列合并成一个最终的排序序列。
在描述归并排序的步骤时又调用了归并排序本身,可见这是一个递归的过程。
下面过程来分析,见下图:
代码实现:
void _MergeSort(int* a,int* tmp,int left,int right)
{
if(left >= right)
return;
int mid=left+((right-left)>>1);
int begin1=left,end1=mid;
int begin2=mid+1,end2=right;
if(left < mid) //left=mid时,单个元素不进行分割
_MergeSort(a,tmp,left,mid);
if(mid < right)
_MergeSort(a,tmp,mid+1,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++];
}
for(int i=left;i<=right;++i)
{
a[i]=tmp[i];
}
}
void MergeSort(int* a,int begin,int end)
{
assert(a);
int* tmp=new int[end-begin+1];
assert(tmp);
_MergeSort(a,tmp,begin,end);
delete[] tmp;
}
归并排序算法改进:
可利用插入排序优化归并排序,
在归并中利用插入排序不仅可以减少递归次数,还可以减少内存分配次数(针对于原始版本)。
尽管归并排序最坏情况运行时间为o(nlgn),插入排序的最坏运行时间为o(n^2),但是插入排序的常数因子使得它在n较小时,运行要更快一些。因此,在合并排序算法中,当子问题足够小时(子区间元素个数小于13),采用插入排序就比较合适了。然后,再用标准的合并机制将它们合并起来。
void InsertSort(int* arr,size_t sz)
{
assert(arr);
for(int index=1;index<sz;++index) //先默认下标为0的元素有序
{
int pos=index-1;
int tmp=arr[index];
while(pos>=0 && tmp < arr[pos])
{
std::swap(arr[pos],arr[pos+1]);
--pos;
}
arr[pos+1]=tmp;
}
}
void _MergeSort(int* a,int* tmp,int left,int right)
{
if(left >= right)
return;
int mid=left+((right-left)>>1);
int begin1=left,end1=mid;
int begin2=mid+1,end2=right;
if(left < mid) //left=mid时,单个元素不进行分割
{
if(right-left >13)
_MergeSort(a,tmp,left,mid);
else
InsertSort(a+left,right-left+1);
}
if(mid+1 < right)
{
if(right-mid-1 >13)
_MergeSort(a,tmp,right,mid);
else
InsertSort(a+mid+1,right-mid); //[mid+1,right]所以元素个数为right-mid
}
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++];
}
for(int i=left;i<=right;++i)
{
a[i]=tmp[i];
}
}
void MergeSort(int* a,int begin,int end)
{
assert(a);
int* tmp=new int[end-begin+1];
assert(tmp);
_MergeSort(a,tmp,begin,end);
delete[] tmp;
}
比较排序算法就先介绍到此,下面看一下它们之间的区别: