一、递归实现归并排序
用递归实现归并排序的思路就是把序列分成不可再分的子序列,合并两个有序的子序列,然后回溯(回溯就是递归结束返回上一层),再合并两个有序的子序列,再回溯再合并……
合并两个有序数列的思路是分成左区间和右区间,a[begin1]和a[begin2]比较,如果a[begin1]比a[begin2]小,就把它放入tmp,然后++begin1,反之亦然……最后如果左区间取完了就把剩余右区间全部放入tmp,反之亦然。
代码如下:
void _MergeSort(int* a, int left, int right,int*tmp)
{
if (left >= right)
{
return;
}
int midindex = (left + right) / 2;
_MergeSort(a, left, midindex,tmp);//归并左区间
_MergeSort(a, midindex+1, right,tmp);//归并右区间
int begin1 = left, end1 = midindex;//[left,midindex]
int begin2 = midindex+1, end2 = right;//[minindex+1,right]
int index = left;
while (begin1 <= end1 && begin2 <= end2)
{
//把两个区间中小的放入tmp,然后++,当其中一个取空了就直接把另一个放入tmp
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 n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
_MergeSort(a, 0, n-1,tmp);
free(tmp);
}
void TestMergeSort()
{
int a[] = { 6,1,2,7,9,3,4,5,10 ,8};
MergeSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
二、非递归实现归并排序
非递归实现归并排序的思路就是定义一个区间间隔(gap),然后合并两个有序区间,然后gap=gap*2,再合并……如上图第一次排序时gap=1,就把7,5当成两个间隔为1的有序区间进行合并;第二次排序时gap=2,就把5 7,1 3当成两个有序区间合并;第三次排序:gap=4,就把1 3 5 7,2 4 6 8当成两个有序区间合并……
另外在合并时我们要注意边界的情况,注意是否有越界或者只有一个区间的情况发生。当只有一个区间时不用合并直接放入下一次排序就好;当越界时,我们要重新修订边界。如下:
代码中:if (begin2 >= n)
{
break;
}
if (end2 >= n)
{
end2 = n - 1;
}
就是对这两种问题的修订
代码如下:
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap<n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i+gap-1;//[i,i+gap-1]
int begin2 = gap + i, end2 =i+2*gap-1 ;//[i+gap,a+2*gap-1]
if (begin2 >= n)
{
break;
}
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
//把两个区间中小的放入tmp,然后++,当其中一个取空了就直接把另一个放入tmp
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 j = 0; j <=end2; j++)//拷贝回原数组
{
a[j] = tmp[j];
}
}
gap *= 2;
}
free(tmp);
}
void TestMergeSortNonR()
{
int a[] = { 6,1,2,7,9,3,4,5,10,8};
MergeSortNonR(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
四、归并排序的时间复杂度及稳定性
归并排序的时间复杂度为O(N*logN),空间复杂度为O(N),具有稳定性