归并排序是一种分治算法,首先把一个数组分成两个子数组,然后递归的对两个子数组进行排序,最后把两个子数组合并起来,问题解决……
递归是一种神奇的技术,所谓人理解循环,神理解递归……我觉得有时候递归很好理解,有时候又很不可思议。虽然这里的代码很长,但是理解这个过程比理解插入排序容易。
还是用扑克牌来举例:有一堆乱序的扑克牌让你去排序,为了让事情更有序更容易,你把牌分成两份,让两个小弟先排好序;两个小弟又分别让他们的两个小小弟去排序;最后的小小小小小弟拿到的是一份已经排好序的牌,只有一张;这样就形成了一棵树……每个小弟把手上的牌排好序就交给自己的老大,老大整理好两组牌再上交给自己的老大……最后到你手里的就是两组已经排好序的牌,然后你再整理一下就完事了。
这个所谓的整理就是合并的过程,归并排序的合并过程首先把两个排好序的子数组各复制一份,再依次从两个子数组中选择最小的一个插入原数组,直到取完所有的牌。这里为了简化代码,在两个新数组的后面各自加入一个哨兵元素,也就是最大可能的值,这样就不必每次检查是不是有哪个数组空了。而要排序的总个数我们是知道的。
其实看代码比看上面的废话更简单:
#include <stdio.h> #include <limits.h> #include "array_sort.h" /** * @brief Merge two sorted sub-sequences * * @param array The input array * @param p Beginning index number of array. * @param q Middle index number of array, end of the first sub-sequence. * @param r Endding index number of array. * @note Attention: p <= q < r */ static void merge (int *array, int p, int q, int r) { int n1 = q - p + 1; int n2 = r - q; int i, j, k; /* alloc extra 1 slot for sentinel */ int la[n1 + 1]; int ra[n2 + 1]; for (i = 0; i < n1; i++) { la[i] = array[p + i]; } for (j = 0; j < n2; j++) { ra[j] = array [q + j + 1]; } la [n1] = INT_MAX; ra [n2] = INT_MAX; i = 0; j = 0; for (k = p; k <= r; k++) { /* even a integer equal to sentiel still works */ if (la[i] <= ra[j]) { array[k] = la[i]; i++; } else { array[k] = ra[j]; j++; } } } /** * @brief Merge sort for array * * Divide: Divede the n-element sequence to be sorted into two subsequences * of n/2 elements each. * Conquer: Sort the two subsequences recursively using merge sort. * Combine: Merger the two sorted subsequences to produce the sorted answer. * * @note Complexity O( n*lg(n) ) * @param array The array need to be sort * @param p Beginning index number of array * @param r Ending index number of array * @return The sorted array. */ int *merge_sort (int *array, int p, int r) { if (p < r) { int q = (p + r) / 2; merge_sort (array, p, q); merge_sort (array, q + 1, r); merge (array, p, q, r); } return array; }