根据Merge Sort原理, 自己实现的归并排序算法+详细注释+代码(C#,C/C++) [分享]
本文是受前面的一篇《C#实现所有经典排序算法》- 飛雪飄寒 影响,应邀请,把我曾经实现的归并排序算法拿出来分享,欢迎改善:
归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。
1. 不多废话,我已经把注释写得很详细了,C#实现的分享如下:
/// <summary>
/// 归并排序之归:归并排序入口
/// Updated by Lihua at 05/06/2009
/// </summary>
/// <param name="data">无序数组</param>
/// <returns>有序数组</returns>
/// <author>lihua</author>
/// <copyright>www.zivsoft.com</copyright>
int[] Sort(int[] data)
{
//若data为null,或只剩下1 or 0个元素,返回,不排序
if (null == data || data.Length <= 1)
{
return data;
}
//取数组中间下标
//int middle = data.Length / 2; //方法一:除2取整数
int middle = data.Length >> 1; //方法二:位移 (感谢读者hyper给出的这个效率稍高的方法)
//初始化临时数组let,right,并定义result作为最终有序数组,若数组元素奇数个,将把多余的那元素空间预留在right临时数组
int[] left = new int[middle], right = new int[data.Length - middle], result = new int[data.Length];
//下面这句对性能有些影响,所以在上面有了改进,直接用data.Length-middle初始化right数组
//if (data.Length % 2 != 0) right = new int[middle + 1];
//int i = 0, j = 0;
//foreach (int x in data)//开始排序
//{
// if (i < middle)//填充左数组
// {
// left[i] = x;
// i++;
// }
// else//填充右数组
// {
// right[j] = x;
// j++;
// }
//}
//上面的foreach被改成了for循环
//for (int i = 0; i < data.Length; i++)
//{
// if (i < middle)//用middle,不用left.Length
// {
// left[i] = data[i];
// }
// else
// {
// right[i - middle] = data[i]; //此处i-middle,让我省掉定义一个j,性能有所提高
// }
//}
//经调查,用Array.Copy的确比上面的for循环优化很多
Array.Copy(data, 0, left, 0, middle);//拷贝左数组
Array.Copy(data, left.Length, right, 0, right.Length); //拷贝右数组
left = Sort(left);//递归左数组
right = Sort(right);//递归右数组
result = Merge(left, right);//开始排序
//this.Write(result);//输出排序,测试用(lihua debug)
return result;
}
/// <summary>
/// 归并排序之并:排序在这一步
/// </summary>
/// <param name="a">左数组</param>
/// <param name="b">右数组</param>
/// <returns>合并左右数组排序后返回</returns>
int[] Merge(int[] a, int[] b)
{
//定义结果数组,用来存储最终结果
int[] result = new int[a.Length + b.Length];
int i = 0, j = 0, k = 0;
while (i < a.Length && j < b.Length)
{
if (a[i] < b[j])//左数组中元素小于右数组中元素
{
result[k++] = a[i++];//将小的那个放到结果数组
}
else//左数组中元素大于右数组中元素
{
result[k++] = b[j++];//将小的那个放到结果数组
}
}
while (i < a.Length)//这里其实是还有左元素,但没有右元素
{
result[k++] = a[i++];
}
while (j < b.Length)//右右元素,无左元素
{
result[k++] = b[j++];
}
return result;//返回结果数组
}
/// 归并排序之归:归并排序入口
/// Updated by Lihua at 05/06/2009
/// </summary>
/// <param name="data">无序数组</param>
/// <returns>有序数组</returns>
/// <author>lihua</author>
/// <copyright>www.zivsoft.com</copyright>
int[] Sort(int[] data)
{
//若data为null,或只剩下1 or 0个元素,返回,不排序
if (null == data || data.Length <= 1)
{
return data;
}
//取数组中间下标
//int middle = data.Length / 2; //方法一:除2取整数
int middle = data.Length >> 1; //方法二:位移 (感谢读者hyper给出的这个效率稍高的方法)
//初始化临时数组let,right,并定义result作为最终有序数组,若数组元素奇数个,将把多余的那元素空间预留在right临时数组
int[] left = new int[middle], right = new int[data.Length - middle], result = new int[data.Length];
//下面这句对性能有些影响,所以在上面有了改进,直接用data.Length-middle初始化right数组
//if (data.Length % 2 != 0) right = new int[middle + 1];
//int i = 0, j = 0;
//foreach (int x in data)//开始排序
//{
// if (i < middle)//填充左数组
// {
// left[i] = x;
// i++;
// }
// else//填充右数组
// {
// right[j] = x;
// j++;
// }
//}
//上面的foreach被改成了for循环
//for (int i = 0; i < data.Length; i++)
//{
// if (i < middle)//用middle,不用left.Length
// {
// left[i] = data[i];
// }
// else
// {
// right[i - middle] = data[i]; //此处i-middle,让我省掉定义一个j,性能有所提高
// }
//}
//经调查,用Array.Copy的确比上面的for循环优化很多
Array.Copy(data, 0, left, 0, middle);//拷贝左数组
Array.Copy(data, left.Length, right, 0, right.Length); //拷贝右数组
left = Sort(left);//递归左数组
right = Sort(right);//递归右数组
result = Merge(left, right);//开始排序
//this.Write(result);//输出排序,测试用(lihua debug)
return result;
}
/// <summary>
/// 归并排序之并:排序在这一步
/// </summary>
/// <param name="a">左数组</param>
/// <param name="b">右数组</param>
/// <returns>合并左右数组排序后返回</returns>
int[] Merge(int[] a, int[] b)
{
//定义结果数组,用来存储最终结果
int[] result = new int[a.Length + b.Length];
int i = 0, j = 0, k = 0;
while (i < a.Length && j < b.Length)
{
if (a[i] < b[j])//左数组中元素小于右数组中元素
{
result[k++] = a[i++];//将小的那个放到结果数组
}
else//左数组中元素大于右数组中元素
{
result[k++] = b[j++];//将小的那个放到结果数组
}
}
while (i < a.Length)//这里其实是还有左元素,但没有右元素
{
result[k++] = a[i++];
}
while (j < b.Length)//右右元素,无左元素
{
result[k++] = b[j++];
}
return result;//返回结果数组
}
2. C/C++实现如下
//归并排序中之并
//Updated by zivsoft at 05/06/2009
int *Merge(int *a,int aLength,int *b,int bLength){
//合并结果指针
int *result;
//初始化结果指针
result=new int[aLength+bLength];
int i=0,j=0,k=0;
//定义左指针
a=new int[aLength];
//定义右指针
b=new int[bLength];
//元素排序,左右比较
while(i<aLength&&j<bLength){
if(a[i]<b[j]){//左元素小于右元素
result[k++]=a[i++];//将小的赋值到结果
}
else{//左元素大于右元素
result[k++]=b[j++];//将小的赋值到结果
}
}
while(i<aLength){//将最后一个元素赋值到结果
result[k++]=a[i++];
}
while(j<bLength){//将最后一个元素赋值到结果
result[k++]=b[j++];
}
return result;
}
//归并排序中之归拆分
//Updated by zivsoft at 05/06/2009
int *Split(int *data,int length){
int i=0,j=0,k=0;
int *left,*right,*result;
//取中间下标
int middle=length/2;
left=new int[middle];
right=new int[middle];
//初始化有序结果数组
result=new int[length];
//如果数组只有一个元素,直接返回,无需排序
if(length<=1){
return data;
}
int rightLength=0;
//奇数个元素的话,重新分配右数组长度
if(length%2!=0){
delete[] right;
rightLength=middle+1;
right=new int[rightLength];
}
//拆分数组
for(k=0;k<length;k++){
if(i<middle){
left[i++]=data[k];
}
else{
right[j++]=data[k];
}
}
left=Split(left,i);//递归拆分左数组
right=Split(right,rightLength);//递归拆分右数组
result=Merge(left,i,right,rightLength);//排序并合并
//printarray(result,k); //输出,供lihua(zorywa)侧使用(zivsoft)
return result;
}
//Updated by zivsoft at 05/06/2009
int *Merge(int *a,int aLength,int *b,int bLength){
//合并结果指针
int *result;
//初始化结果指针
result=new int[aLength+bLength];
int i=0,j=0,k=0;
//定义左指针
a=new int[aLength];
//定义右指针
b=new int[bLength];
//元素排序,左右比较
while(i<aLength&&j<bLength){
if(a[i]<b[j]){//左元素小于右元素
result[k++]=a[i++];//将小的赋值到结果
}
else{//左元素大于右元素
result[k++]=b[j++];//将小的赋值到结果
}
}
while(i<aLength){//将最后一个元素赋值到结果
result[k++]=a[i++];
}
while(j<bLength){//将最后一个元素赋值到结果
result[k++]=b[j++];
}
return result;
}
//归并排序中之归拆分
//Updated by zivsoft at 05/06/2009
int *Split(int *data,int length){
int i=0,j=0,k=0;
int *left,*right,*result;
//取中间下标
int middle=length/2;
left=new int[middle];
right=new int[middle];
//初始化有序结果数组
result=new int[length];
//如果数组只有一个元素,直接返回,无需排序
if(length<=1){
return data;
}
int rightLength=0;
//奇数个元素的话,重新分配右数组长度
if(length%2!=0){
delete[] right;
rightLength=middle+1;
right=new int[rightLength];
}
//拆分数组
for(k=0;k<length;k++){
if(i<middle){
left[i++]=data[k];
}
else{
right[j++]=data[k];
}
}
left=Split(left,i);//递归拆分左数组
right=Split(right,rightLength);//递归拆分右数组
result=Merge(left,i,right,rightLength);//排序并合并
//printarray(result,k); //输出,供lihua(zorywa)侧使用(zivsoft)
return result;
}
感言:
这个算法是我在XX的一个面试题,问如何高效率将两个链表合并,并使之有序。很快想到归并排序(Merge Sort),想了想原理,把它实现了,不过现在又把它拿出来加了很多注释,应该没什么大问题了吧,重要是理解算法的精髓,用什么实现应该差不多,目前写了C#和C/C++两个版本,便于理解Merge Sort的原理,欢迎提建议,共同提高。 :-)
本文链接:http://www.cnblogs.com/architect/archive/2009/05/06/1450489.html
关于作者:http://www.zivsoft.com/