归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
归并排序思想:
多次将两个或者两个以上的有序表合并成一个新的有序表。
最简单的归并:是直接将两个有序的子表合并成一个有序的表。
最简单的归并,又被称为2-路归并
排序前:
排序后:
void Merge(int R[] ,int low,int mid,int high)
{
int * R1;
int i = low,j = mid+1 , k= =0;
//动态分配空间R1,用于保存合并结果
R1 = (int *)malloc((high - low +1)*sizeof(int));
//两段均未扫描完成时合并
while(i <= mid && j <=high)
{
if(R[i] < R[j])
{
R1[k] = R[i];
i++;
k++;
}
else
{
R1[k] = R[j];
j++;
k++;
}
}
//将第一段剩余的部分复制到R1
while( i <= mid)
{
R1[k] = R[i];
i++;
k++;
}
//将第二段余下的部分复制到R1
while(j <= high)
{
R1[k] = R[j];
j++;
k++;
}
//将合并后的结果复制回R
for(i = 0 ;i < high;i++)
R[low+i] = R1[i];
}
归并排序的过程:
如何获取到两个已经有序的数列呢?
开始的时候,把单个元素作为一个序列,然后把相邻的两个元素合并为一个有序的队列。
即:第一趟,把相邻的两个元素合并为一个有序的序列。合并后序列长度为2.
第二趟:把第一趟合并完的有序序列看成一个整体,和相邻的有序序列合并。合并后序列的长度为4
第三趟:同上,合并后序列的长度为8.
根据上面的描述,代码如下:
void MergeSort(int R[],int n)
{
int length;
//合并的长度从1开始,每次都乘以2
for(length = 1; length <n;length = 2*length)
{
//进行一趟合并
MergePass(R,length,n);
}
//如果前面合并完后,还有剩余的序列,比如上面的1 5序列,则把剩余的序列和前面合并好的序列继续合并
if(i+length -1 <n)
Merge(R,i+length-1,n-1);
}
void MergePass(int R[],int length,int n)
{
int i;
//i+2*length-1 <n 是为了保证 最后合并的两个序列的下标,不会大于序列的实际的下标。
for(i = 0; i+2*length-1 <n; i+=2*length)
{
Merge(R,i,i+length-1;i+2*length-1);
}
}
感觉上面的代码好复杂啊。
网上看到了一个比较简单的代码:
void mergeSort(int R[], int first, int last)
{
if(first < last)
{
int mid = (first+last)/2;
mergeSort(R,first,mid); //左边有序
mergeSort(R,mid+1,last); //右边有序
Merge(R,first,mid,last);//再将两个有序的序列合并
}
}
虽然代码简单了,但是原理还是上面说到的原理。当分出来的小组只有一个数据时,可以认为小组内已经达到有序,然后再合并相邻的两个小组,就可以了。
这样通过先递归的分解数列,再合并数列就完成了归并排序。