归并排序是递归排序的一种,所以今天我们就来浅谈一下归并排序的递归方式和非递归方式。
归并排序的原理
归并排序的原理就是先拆分,然后再归并。可能很抽象,但是假如我们从中间点开始,左边有序,右边也有序,那么将左边和右边归并即可,那么我们怎么知道左右有序呢?那如果左边和右边只剩下一个数了,是不是就相当于有序了呢?如图:
那么递归我们解决了,归并怎么做到呢?这里我们需要新开一个数组,存放我们已经有序的数,并且在最后一次的时候完整复制回去。如图:只是一趟左递归。
所以由图可见,我们需要先找中间数,然后递归,然后再归并这个顺序。
归并排序的实现(内有详细注释)
void _MergeSort(int *a,int begin,int end,int *tmp)
{
if (begin >= end)
{
//递归结束标志,当只剩下一个数的时候就不需要在递归了
//一个数的时候已经是有序了
return;
}
int mid = (begin + end) / 2;//找中间数
//[begin , mid] [mid+1 , end]区间
//递归分区间
_MergeSort(a, begin, mid,tmp);//左区间
_MergeSort(a, mid+1 ,end, tmp);//右区间
int begin1 = begin;
int end1 = mid ;//左区间
int begin2 = mid + 1;
int end2 = end;//右区间
int i = begin;//i是我们新建数组的下标
//假设在右半边,开始的下标不是0,所以这里i不能为0
while (begin1 <= end1 && begin2 <= end2)
{
//升序,尾插
if (a[begin1] <= a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
//如果左右区间有一个区间已经尾插完了,将剩下的直接尾插就好
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a+begin, tmp+begin, sizeof(int) * (end - begin + 1));
//为什么要加begin,是因为每次开始不一定都是从0开始的,所以需要找每次开头的数组位置,所以要加begin
}
void MergeSort(int* a, int n)
{
//为什么要重写一个_MergeSort而不直接递归呢?
//因为我们要开空间来递归用,但是递归的话每次都会开空间!不能每次都开空间!
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
_MergeSort(a,0,n-1,tmp);//具体实现
free(tmp);
}
归并排序的非递归方式
归并排序是后序递归方式,所以如果使用平常的栈或队列是不行的!栈是可以完成递归,但并不能做到归并。所以我们需要利用循环的方式来实现。
实现方式:因为我们提到一个的时候是有序,所以我们可以按分组的方式来gap=1的时候,一个一个归并,gap=2的时候两个两个归并…
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1;//从1开始分
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//为什么是i += 2 * gap?
//因为以begin1-end1 和begin2-end2这个区间已经归完了
//我们需要跳到下一个区间内
int begin1 = i;
int end1 = i + gap - 1;
//为什么-1?因为是下标
int begin2 = i + gap;
int end2 = i + 2 * gap - 1;
if (end1 >= n || begin2 >= n)
{
//会存在越界情况
break;
}
if (end2 >= n)
{
end2 = n - 1;
}
int j = begin1;
//tmp的下标
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[j++] = a[begin1++];
}
else
{
tmp[j++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = a[begin2++];
}
memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
}
gap *= 2;//由图可得每次gap都是2倍增加
}
free(tmp);
}