【算法】归并排序

来源:互联网 发布:2017恩智浦智能车算法 编辑:程序博客网 时间:2024/06/07 02:34

归并排序的核心:两个有序子列的归并

(图片来自 中国大学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++];elsetmp[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);}elseprintf("空间不足!\n");}
至此,整个算法就实现完成了!

最后附上我测试用的代码:

#include <stdio.h>#include <stdlib.h>#define N 1000int 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;}


1 0
原创粉丝点击