归并排序的核心:两个有序子列的归并
(图片来自 中国大学MOOC《数据结构》)
如上所示,假如我们已经有了这样两个有序的子序列,首先要做的就是把这两个子列合并。为此,可以开另外一个数组,然后把这几个数字按照一定次序放进去,保证最后那个数组是从小到大有序的。
如上,我们分别用三个指针指向这几个数组的开始(注意:这里的指针不是C语言里明确定义的那个指针,而是我们借用它指向了一个地址,其不一定为指针,例如,数组下标)
接下来要做的就很简单了,比较Aptr 和 Bptr所指值的大小,1<2,所以把1放入Cptr所指的位置,然后Aptr和Cptr均向右移一位,如下图所示:
剩余的事情是重复的,就不再讲了,现在,就用具体的函数将其实现:
void merge(int elem[], int tmp[], int left, int right, int right_end) //归并函数,只负责归并两个有序子列 { //elem为原始数据,包含两个子序列。left为子序列1的起点,right_end为子序列2的终点 int i = 0; int left_end = right - 1; //子序列1的最右端 int t = left; //临时数组的起点 int nums = right_end - left + 1; //总元素个数 while (left <= left_end && right <= right_end) { //仅当两个子序列都有元素,进入循环 if (elem[left] <= elem[right]) tmp[t++] = elem[left++]; else tmp[t++] = elem[right++]; } //此循环退出后,必有一个序列还有剩余元素 while (left <= left_end) //若序列1有剩余,直接依次放入临时数组 tmp[t++] = elem[left++]; while (right <= right_end) //若序列2有=剩余,直接依次放入临时数组,这两个while循环只有一个会进入循环体执行 tmp[t++] = elem[right++]; for (i = 0; i < nums; i++, right_end--) //再把tmp的元素倒回到原始数组 elem[right_end] = tmp[right_end]; }
然后具体实现这个归并排序的递归算法:
现在,核心的算法已经实现了。但是,这个函数每次调用的时候都要=传一大堆的参数进去,是很不友好的。所以我们可以给它写一个壳,提供一个统一的接口:void m_sort(int elem[], int tmp[], int left, int right_end) { int center; if (left < right_end) { //当满足数组有元素的时候进入,当元素个数为1时(left==right_end),则返回,因为只有一个元素,必定有序 center = (right_end - left) / 2 + left; //求取中心元素的位置 m_sort(elem, tmp, left, center); //调用自身对左半边进行排序 m_sort(elem, tmp, center + 1, right_end); //对右半边进行排序 merge(elem, tmp, left, center + 1, right_end); //将两个有序子列归并到一块,调用了我们上边实现的函数 } }
至此,整个算法就实现完成了!void merge_sort(int elem, int n) //只留下两个参数,待排数组和元素个数 { int *tmp = (int *)malloc(n * sizeof(int)); if (tmp) { m_sort(elem, tmp, 0, n - 1); free(tmp); } else printf("空间不足!\n"); }
最后附上我测试用的代码:
#include <stdio.h> #include <stdlib.h> #define N 1000 int main(void) { int arr[N] = {0}; int i = 0, j = 1000; for (i = 0; i < N; i++) { arr[i] = j--; } merge_sort(arr, N); for (i = 0; i < N; i++) { printf("%d ", arr[i]); } system("pause"); return 0; }
【算法】归并排序
最新推荐文章于 2024-08-12 19:32:44 发布