算法原理
归并排序(Merge Sort)是利用“归并” 技术来进行排序。归并是指将若干个已排序的 子 文件 合并 成 一个 有序 的 文件。
两路 归并 算法 的 基本 思路: 设 两个 有序 的 子 文件( 相当于 输入 堆) 放在 同一 向量 中 相邻 的 位置 上: A[ low… m], a[ m+ 1… high], 先 将它 们 合并 到 一个 局部 的 暂存 向量 Temp( 相当于 输出 堆) 中, 待 合并 完成 后 将 Temp 复制 回 A[ low... high] 中。
归并 排序 有两 种 实现 方法: 自 底 向上 和 自 顶 向下。
自 底 向上 方法 的 基本 思想:
(1) 第 1 趟 归并 排序 时, 将 待 排序 的 文件 A[ 1... n] 看作 n 个 长度 为 1 的 有序 子 文件, 将 这些 子 文件 两两 归并。 若 n 为 偶数, 则 得到 n/ 2 个 长度 为 2 的 有序 子 文件; 若 n 为 奇数, 则 最后 一个 子 文件 不 参与 归并。 故 本 趟 归并 完成 后, 前 n/ 2 个 有序 子 文件 长度 为 2, 但 最后 一个 子 文件 长度 仍 为 1。
(2) 第 2 趟 归并 则 是将 第 1 趟 归并 所 得到 的 n/ 2 个 有序 的 子 文件 两两 归并, 如此 反复, 直到 最后 得到 一个 长度 为 n 的 有序 文件 为止。
(3) 上述 每次 归并 操作, 均 是将 两个 有序 的 子 文件 合并 成 一个 有序 的 子 文件, 故称 其为“ 二路 归并 排序”。 类似 地, 有 k( k> 2) 路 归并 排序。
自 顶 向下 算法 的 设计, 形式 更为 简洁。 设 归并 排序 的 当前 区间 是 A[ low... high], 步骤 如下。
(1) 分解: 将 当前 区间 一分为二, 即 求 分裂 点。
(2) 求解: 递归 地 对 两个 子 区间 A[ low... mid] 和 A[ mid+ 1... high] 进行 归并 排序。
(3) 组合: 将 已 排序 的 两个 子 区间 A[ low... mid] 和 A[ mid+ 1... high] 归并 为 一个 有序 的 区间 R[ low… high]。
(4) 递归 的 终结 条件: 子 区间 长度 为 1( 一个 记录 自然 有序)。
算法草稿
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SUCCESS 0
#define PARAM_ERR -1
#define ALLOC_ERR -2
/*
* 合并两个子数组 [lstart, lend] [rstart, rend]
*/
int MergeImpl(int * array, int * temp, int lstart, int lend, int rstart, int rend){
if(NULL == array || NULL == temp){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
#if 1
int size = 0; /* 两个待合并的素组总长度 */
int i = 0, j = 0, k = 0;
size = (lend - lstart + 1) + (rend - rstart + 1);
i = lstart;
j = rstart;
k = 0;
#ifdef DEBUG
int m = 0;
printf("Befor %s: size = %d \n", __func__, size);
for(m = lstart; m <= rend ; m++){
printf("array[%d] = %d ", m, array[m]);
}
printf("\n");
#endif
/*清空待合并数组*/
memset(temp, 0x0, size * sizeof(int));
/* 升序 */
while ( (i <= lend) && (j <= rend)){
if(array[i] <= array[j]){
temp[k++] = array[i++];
} else {
temp[k++] = array[j++];
}
}
/* 左子组还有 */
while (i <= lend){
temp[k++] = array[i++];
}
/* 右子组还有 */
while (j <= rend){
temp[k++] = array[j++];
}
/*
* 合并后的数组,复制回array
* 因为左右两个子组是相邻的,所以从lstart顺序复制如array
*/
i = lstart;
for(k = 0; k < size; k++){
array[i++] = temp[k];
}
#ifdef DEBUG
printf("After: %s \n", __func__);
for(m = 0; m < size ; m++){
printf("temp[%d] = %d ", m, temp[m]);
}
printf("\n");
for(m = lstart; m <= rend ; m++){
printf("array[%d] = %d ", m, array[m]);
}
printf("\n\n");
#endif
#else
int size = 0; /* 两个待合并的素组总长度 */
int lpos = 0, rpos = 0, tpos = 0, k = 0;
size = rend - lstart + 1;
lpos = lstart;
rpos = rstart;
tpos = lstart;
/* 升序 */
while ( (lpos <= lend) && (rpos <= rend)){
if(array[lpos] <= array[rpos]){
temp[tpos++] = array[lpos++];
} else {
temp[tpos++] = array[rpos++];
}
}
/* 左子组还有 */
while (lpos <= lend){
temp[tpos++] = array[lpos++];
}
/* 右子组还有 */
while (rpos <= rend){
temp[tpos++] = array[rpos++];
}
/*
* 合并后的数组,复制回array
* 因为左右两个子组是相邻的,所以从lstart顺序复制如array
*/
for(k = 0; k < size; k++, rend--){
array[rend] = temp[rend];
}
#endif
return SUCCESS;
}
/*
* 本函数真正的合并排序实现
*/
int MergeSortImpl(int * array, int * temp, int low, int high){
if(NULL == array || NULL == temp){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
int i = 0, j = 0;
int size = 0; /* 排序分组大小 */
int mid = 0;
size = high - low + 1;
/* 递归终止条件,数组中只有一个元素*/
if(size <= 1){
goto out;
}
/*
* 两路合并排序,自定向下,取得中间点
* 注意 low 和 high 的中间的不是 size /2 而是 (low + high) / 2 ! 千万注意
*/
mid = (low + high) / 2; /*左子组:[low, mid], 右子组:[mid+1, high]*/
/* 对左子组两路合并排序*/
MergeSortImpl(array, temp, low, mid);
/* 对右子组两路合并排序*/
MergeSortImpl(array, temp, mid + 1 , high);
/* 对已排序的两个子组进行合并 */
MergeImpl(array, temp, low, mid, mid + 1, high);
out:
return SUCCESS;
}
int MergeSort(int * array, int size){
if(NULL == array){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
int * temp = NULL;
/*这里是合并排序的接口,完成准备工作*/
temp = (int *) malloc (size * sizeof(int));
if(NULL == temp){
printf("%s alloc memory error\n",__func__);
return ALLOC_ERR;
}
MergeSortImpl(array, temp, 0, size - 1);
/* 删除已分配空间 */
free(temp);
temp = NULL;
return SUCCESS;
}
int main(int argc, char ** argv){
int array[10] = {7,3,5,8,0,9,1,2,4,6};
int i = 0;
printf("Before sort: \n");
for(i = 0; i < 10; i++){
printf(" %d ", array[i]);
}
printf("\n");
MergeSort(array, 10);
printf("after sort: \n");
for(i = 0; i < 10; i++){
printf(" %d ", array[i]);
}
printf("\n");
return 0;
}
调试编译
gcc MergeSort.c -DDEBUG
调试输出
Before sort:
7 3 5 8 0 9 1 2 4 6
Befor MergeImpl: size = 2
array[0] = 7 array[1] = 3
After: MergeImpl
temp[0] = 3 temp[1] = 7
array[0] = 3 array[1] = 7
Befor MergeImpl: size = 3
array[0] = 3 array[1] = 7 array[2] = 5
After: MergeImpl
temp[0] = 3 temp[1] = 5 temp[2] = 7
array[0] = 3 array[1] = 5 array[2] = 7
Befor MergeImpl: size = 2
array[3] = 8 array[4] = 0
After: MergeImpl
temp[0] = 0 temp[1] = 8
array[3] = 0 array[4] = 8
Befor MergeImpl: size = 5
array[0] = 3 array[1] = 5 array[2] = 7 array[3] = 0 array[4] = 8
After: MergeImpl
temp[0] = 0 temp[1] = 3 temp[2] = 5 temp[3] = 7 temp[4] = 8
array[0] = 0 array[1] = 3 array[2] = 5 array[3] = 7 array[4] = 8
Befor MergeImpl: size = 2
array[5] = 9 array[6] = 1
After: MergeImpl
temp[0] = 1 temp[1] = 9
array[5] = 1 array[6] = 9
Befor MergeImpl: size = 3
array[5] = 1 array[6] = 9 array[7] = 2
After: MergeImpl
temp[0] = 1 temp[1] = 2 temp[2] = 9
array[5] = 1 array[6] = 2 array[7] = 9
Befor MergeImpl: size = 2
array[8] = 4 array[9] = 6
After: MergeImpl
temp[0] = 4 temp[1] = 6
array[8] = 4 array[9] = 6
Befor MergeImpl: size = 5
array[5] = 1 array[6] = 2 array[7] = 9 array[8] = 4 array[9] = 6
After: MergeImpl
temp[0] = 1 temp[1] = 2 temp[2] = 4 temp[3] = 6 temp[4] = 9
array[5] = 1 array[6] = 2 array[7] = 4 array[8] = 6 array[9] = 9
Befor MergeImpl: size = 10
array[0] = 0 array[1] = 3 array[2] = 5 array[3] = 7 array[4] = 8 array[5] = 1 array[6] = 2 array[7] = 4 array[8] = 6 array[9] = 9
After: MergeImpl
temp[0] = 0 temp[1] = 1 temp[2] = 2 temp[3] = 3 temp[4] = 4 temp[5] = 5 temp[6] = 6 temp[7] = 7 temp[8] = 8 temp[9] = 9
array[0] = 0 array[1] = 1 array[2] = 2 array[3] = 3 array[4] = 4 array[5] = 5 array[6] = 6 array[7] = 7 array[8] = 8 array[9] = 9
after sort:
0 1 2 3 4 5 6 7 8 9