> 文章列表 > 排序(4)——归并排序

排序(4)——归并排序

排序(4)——归并排序

目录

前言

1.归并排序的递归实现

1.1 归并排序概念

1.2 归并排序递归实现

2.归并排序的非递归实现


前言

今天给大家带来比较排序的最后一种,归并排序,这个排序,需要我们对递归,循环控制要有着较强的理解,我相信大家通过前面和小编的一起学习,这里肯定也是得心应手。


1.归并排序的递归实现

1.1 归并排序概念

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

1.2 归并排序递归实现

这里我们看具体过程:

这里有个细节我们需要注意一下,在我们对数组进行排序的时候我们需要利用另外一个数组,然后在复制回原数组。

 这里我们的具体代码如下:

void _MergeSort(int* a, int*temp,int begin, int end)
{if (begin >= end){return;}int mid = (begin + end) / 2;_MergeSort(a, temp, begin, mid);_MergeSort(a, temp, mid + 1, end);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] >=a[begin2]){temp[i++] = a[begin2];begin2++;}else{temp[i++] = a[begin1];begin1++;}}while (begin1 <= end1){temp[i++] = a[begin1];begin1++;}while (begin2 <= end2){temp[i++] = a[begin2];begin2++;}memcpy(a + begin, temp + begin, sizeof(int)*(end - begin + 1));
}//归并排序
void MergeSort(int* a,int n)
{int* temp = (int*)malloc(sizeof(int) * n);if (temp == NULL){perror("malloc fail");return;}_MergeSort(a, temp, 0, n - 1);free(temp);temp = NULL;
}

这里由于我们需要使用到另外的数组去进行存储排序,但是由于局部变量在每次函数调用结束后会自动销毁,所以这里我们需要把数组建立在堆区,但是我们创建数组的操作是不能进行递归的,如果递归就会不断出现重复的数组,因此我们这里只需要把排序和复制回元组的操作进行递归,因此这里我们封装函数MergeSort以及函数_MergeSort.

由于这里我们是将数组元素不断分解直到剩一个元素,或者没有元素,然后先进行左右分解区间的排序,最后返回上一级,直到所有的元素都排序完毕,我们就直接结束排序。

这里我给大家详细讲解一下代码:

 

2.归并排序的非递归实现

对于非递归实现,之前小编给大家讲解了快速排序的非递归实现,但是相对于今天的归并排序,前者和树的前序遍历类似,后者和树的后序遍历类似,对于此类的递归我们,一般是从最小空间到最大空间不断控制,但是这个过程中有很多的细节需要我们自己掌握一下。

这里小编给大家演示一下具体过程:

 这里我们发现gap=gap*2,只有当我们的数组长度等于2的倍数的时候我们才能刚好分组,否则就会出现某些问题,例如:

那么对于这里的越界我们一共可能会出现三种越界情况,我们要对其分别进行控制

具体情况如下:

1.end1越界了,不归并

2.end1没有越界,begin2越界了,也不归并

3.end1没有越界,begin也没越界。end2越界了,我们需要修正end2

这里我们该怎么理解呢?首先我们需要知道一点,我们的分组是在小组已经排好序的情况下分的,所以我们要明白,begin1~end1这段区间是有序的,begin2~end2这段区间是有序的,但是begin1~end2这段不一定有序(如果都存在的话),那么对于情况一,如果end1越界说明这两段区间都不存在,所以我们就不需要将其归并,对于情况二,如果begin2越界了,但是end1没越界,由于begin1~end1,已经有序,所以我们也就不需要归并,对于情况三,如果只是begin2~end2部分越界,那么我们只需要将begin1~end1和begin2到数组末尾出现的元素进行归并即可。

那么我们的代码如下:

void MergeSortNonR2(int* a, int n)
{int* temp = (int*)malloc(sizeof(int) * n);if (temp == NULL){perror("malloc fail");return;}int gap = 1;int i = 0;while (gap < n){int j = 0;for (i = 0; i < n; i = i + 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + gap * 2 - 1;if (end1 >n-1&&begin2 > n - 1){break;}else if(end2>n-1){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){temp[j++] = a[begin1++];}else{temp[j++] = a[begin2++];}}while (begin1 <= end1){temp[j++] = a[begin1++];}while (begin2 <= end2){temp[j++] = a[begin2++];}memcpy(a+i, temp+i, sizeof(int)*(end2-i+1));}gap = gap * 2;}}

 代码详解如下: