归并排序很好的体现了分治策略的思想,即将问题分解成同样但事实规模更小的子问题。比如说这里的归并排序,就是有序子列的并归。
如上图 将两个子序列合并到一个temp 数组(这里是暂存的作用),需要三个指针Aptr 与 Bptr 相比较 小的插入暂存数组Cptr , 随着向后移动。代码如下
void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd)
{ /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */
int LeftEnd, NumElements, Tmp;
int i; LeftEnd = R - 1; /* 左边终点位置 */
Tmp = L; /* 有序序列的起始位置 */
NumElements = RightEnd - L + 1; while (L <= LeftEnd && R <= RightEnd) {
if (A[L] <= A[R])
TmpA[Tmp++] = A[L++]; /* 将左边元素复制到TmpA */
else
TmpA[Tmp++] = A[R++]; /* 将右边元素复制到TmpA */
} while (L <= LeftEnd)
TmpA[Tmp++] = A[L++]; /* 直接复制左边剩下的 */
while (R <= RightEnd)
TmpA[Tmp++] = A[R++]; /* 直接复制右边剩下的 */ for (i = 0; i < NumElements; i++, RightEnd--)
A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */
}
后面分为递归的方式与非递归。
我们先来看一下递归的算法:
void Msort(ElementType A[], ElementType TmpA[], int L, int RightEnd )
{ /* 核心递归排序函数 */
int Center;
if (L < RightEnd ) {
Center = (L + RightEnd) / 2;
Msort(A, TmpA, L, Center ); /* 递归解决左边 */
Msort(A, TmpA, Center + 1, RightEnd ); /* 递归解决右边 */
Merge(A, TmpA, L, Center + 1, RightEnd ); /* 合并两段有序序列 */
}
}
void MergeSort(ElementType A[], int N )
{ /* 归并排序 */
ElementType *TmpA;
TmpA = (ElementType *)malloc(N * sizeof(ElementType));
if (TmpA != NULL ) {
Msort(A, TmpA, 0, N - 1);
free(TmpA );
}
else printf("空间不足");
}
递归的算法很直观的表示了分治策略。 即将规模化小。在MergeSort里面,定义了一个方法的接口,用户只需要传入,排序的数组,tempA数组的分配是在次方法里面进行的。
调用过程可以如下图表示
在接下来我们看看非递归的方式。即用循环来代替递归调用。
代码如下:
先看原理图:
这里需要额外的空间来暂存排序结果。但是实际上只要一个tempA数组就好,tempA与A数组之间相互copy 来实现暂存。只是在最后要copy到A数组中罢了。
在Marge_Sort中length = 1,即表示最小的有序子序列是1个数字。这个很好理解。Marge_Pass 就是将数组按 length的长度跳跃扫描一遍。用marge来合并成有序的上一序列。Marge_Sort 有两次次 Marge_Pass的调用。这是为了将tempA,中数组赋值个给A。