归并排序是一种稳定的排序方法,它的中心思想是将两个或者两个以上的有序序列合成一个新的有序表,通过不断的合并得到最终的有序表。最常用的是将2个子序列不断的合并,也就是2路归并排序。
归并排序要求待排序的子序列一定要有序,如果无序怎么办呢,那就把子序列不断的划分的更细,当子序列中只包含一个元素的时候,那么它肯定是有序的,此时把子序列两两归并排序,便可得到最终的有序序列。
归并排序有递归方法与非递归方法
首先来看递归的归并排序:
//将两个有序的子序列归并为一个有序序列
//SR[]为待排序的序列,TR[]用于存放已经排序好的序列
//i为SR[]序列的起始下标,m为第一个子序列结束时的下标,n为SR[]的序列长度
void Merge(int SR[],int TR[],int i,int m,int n)
{
int j = 0; //j为第二个子序列的起始下标
int k = 0; //k用于记录TR[]序列已经排好的下标
int l = 0; //l用于排好一个子序列后另一个子序列的补充长度
//第一个子序列和第二个子序列按位依次比较,把较小的数放入TR[]数组中,直到第一个或者第二个子序列结束跳出循环
for(k = i, j = m +1; i <=m && j <=n; k++){
if(SR[i] < SR[j]){
TR[k] = SR[i];
i++;
}
else{
TR[k] =SR[j];
j++;
}
}
//比较完成后,如果第1个子序列长度有剩余,则将剩余的数据依次添加到TR[]中
if(i <= m)
{
for(l =0; l <= m - i; l++)
TR[k + 1] = SR[i +1];
}
//如果第2个子序列长度有剩余,则将剩余的数据依次添加到TR[]中
if(j <= n)
{
for(l =0;l <= n - j; l++)
TR[k + 1] = SR[j +1];
}
}
//SR[]为待排序序列,TR1[]用于存放最后已经排序好的序列
//s为SR[]需要排序的起始下标,t为SR[]需要排序的终止下标
void MSort (int SR[],int TR[],int s,int t)
{
int m;
int TR2[t +1]; //需要额外的t+1的内存空间记录数据
if (s == t) {
TR[s] = SR[s];
}
else
{
m = (s +t) / 2; //将SR[]平分为两部分
MSort(SR, TR2, s, m); //将前半部分继续递归划分至有序的TR2[](前半部分)
MSort(SR, TR2, m +1, t); //将后半部分继续递归划分至有序的TR2[](后半部分)
Merge(TR2, TR, s, m, t); //将部分有序的TR2[]整合成一个有序的TR1[]
}
}
//传入参数为待排序的序列SR[]以及序列长度length
void MergeSort(int SR[],int length)
{
int TR[length +1]; //申请一块额外的内存空间
int k =1; //k代表每次进行两两归并的数据长度,肯定是2的倍数
while (k < length) {
MergePass(SR, TR, k, length); //把SR的数据两两归并到TR
k = k * 2;
MergePass(TR, SR, k, length); //再把TR的数据归并会SR,就是两块内存来回用
k = k * 2;
}
}
//将SR的数据归并到TR,归并的子长度为s,数据总长度为length
void MergePass(int SR[],int TR[],int s,int length)
{
int i =1;
int j;
while(i <= length - (2 * s -1)) //判断如果数组中的数据为奇数,则最后一个数据不做归并处理
{
Merge(SR, TR, i, i + s -1, i +2 * s -1);//第一个子序列起始下标为i,结束下标为i + s - 1
i = i + 2 * s;
}
if (i < length - s +1) //说明剩下的不是一个子序列(此处是一个长度为s序列一个长度小于s的序列)
{
Merge(SR, TR, i, i + s -1, length); //对这两个子序列进行归并
}
else //如果只剩了一个子序列
{
for (j = i; j <= length; j++) //直接把子序列复制到TR中
{
TR[j] = SR[j];
}
}
}