将两个按值有序序列合并成一个按值有序序列,则称之为二路归并排序。
/**
* 实验题目:
* 实现二路归并排序算法
* 实验目的:
* 领会二路归并排序的过程和算法设计
* 实验内容:
* 设计程序,实现二路归并排序算法。用相关数据进行测试,并
* 输出各趟的排序结果。
*/
#include <stdio.h>
#include <malloc.h>
#define MAX_LEN (100) // 最大长度
typedef int key_type; // 定义关键字类型为int
typedef char info_type;
typedef struct
{
key_type key; // 关键字项
info_type data; // 其他数据项,类型为info_type
}rec_type; // 查找元素的类型
/*-----------------x和y交换------------------*/
void swap_rec(rec_type &x, rec_type &y) // 引用类型
{
rec_type tmp = x;
x = y;
y = tmp;
}
/*-----------------创建顺序表------------------*/
void create_list(rec_type recs[], key_type keys[], int n)
{
int i;
for(i = 0; i < n; i++) // recs[0...n-1]存放排序记录
recs[i].key = keys[i];
}
/*-----------------输出顺序表------------------*/
void disp_list(rec_type recs[], int n)
{
int i;
for(i = 0; i < n; i++)
printf("%d ", recs[i].key);
printf("\n");
}
/*-----------------以下运算针对堆排序的程序------------------*/
/*-----------------创建顺序表------------------*/
void create_list1(rec_type recs[], key_type keys[], int n)
{
int i;
for(i = 1; i <= n; i++) // recs[1...n]存放排序记录
{
recs[i].key = keys[i - 1];
}
}
/*-----------------输出顺序表------------------*/
void disp_list1(rec_type recs[], int n)
{
int i;
for(i = 1; i <= n; i++)
{
printf("%d ", recs[i].key);
}
printf("\n");
}
int cnt = 1; // 全局变量
/**
* 一次归并:
* 将两个有序表recs[low...mid]和recs[mid+1...high]归并为一个有序表recs[low...high]
*
*/
static void merge_recs(rec_type recs[], int low, int mid, int high)
{
rec_type *rec1;
int i = low;
int j = mid + 1;
int k = 0; // k是rec1的下标,i,j分别为第1、2段的下标
printf("low = %d, mid = %d, high = %d\n", low, mid, high);
rec1 = (rec_type *)malloc((high - low + 1) * sizeof(rec_type)); // 动态分配空间
while(i <= mid && j <= high)
{
if(recs[i].key <= recs[j].key) // 将第1段中的记录放入rec1中
{
rec1[k] = recs[i];
i++;
k++;
}
else // 将第2段中的记录放入rec1中
{
rec1[k] = recs[j];
j++;
k++;
}
}
while(i <= mid) // 将第1段余下部分复制到rec1
{
rec1[k] = recs[i];
i++;
k++;
}
while(j <= high) // 将第2段余下部分复制到rec1
{
rec1[k] = recs[j];
j++;
k++;
}
for(k = 0, i = low; i <= high; k++, i++) // 将rec1复制回recs中
recs[i] = rec1[k];
}
/*----------------实现有序表长度为len的一趟归并----------------*/
static void merge_pass(rec_type recs[], int len, int n)
{
int i;
printf("第%d趟归并:\n", cnt++);
for(i = 0; i + 2 * len - 1 < n; i = i + 2 * len) // 归并len长的两相邻子表
{
printf("recs[%d,%d]和recs[%d,%d]归并: ", i, i + len - 1, i + len, i + 2 * len - 1);
merge_recs(recs, i, i + len - 1, i + 2 * len - 1);
}
if(i + len - 1 < n - 1) // 余下两个子表,后者长度小于len
{
printf("*recs[%d,%d]和recs[%d,%d]归并: ", i, i + len - 1, i + len, n - 1);
merge_recs(recs, i, i + len - 1, n - 1); // 归并这两个子表
}
printf("\n归并结果: ");
disp_list(recs, n); // 输出该趟的排序结果
}
/*----------------二路归并排序算法-------------------*/
static void merge_sort(rec_type recs[], int n)
{
int len;
for(len = 1; len < n; len = 2 * len) // len = 1, 2, 4, 8
merge_pass(recs, len, n);
}
int main(int argc, char *argv[])
{
int n = 11;
rec_type recs[MAX_LEN];
key_type a[] = {18, 2, 20, 34, 12, 32, 6, 16, 5, 8, 1};
create_list(recs, a, n);
printf("排序前: ");
disp_list(recs, n);
merge_sort(recs, n);
printf("排序后: ");
disp_list(recs, n);
return 0;
}
测试结果:
排序前: 18 2 20 34 12 32 6 16 5 8 1
第1趟归并:
recs[0,0]和recs[1,1]归并: low = 0, mid = 0, high = 1
recs[2,2]和recs[3,3]归并: low = 2, mid = 2, high = 3
recs[4,4]和recs[5,5]归并: low = 4, mid = 4, high = 5
recs[6,6]和recs[7,7]归并: low = 6, mid = 6, high = 7
recs[8,8]和recs[9,9]归并: low = 8, mid = 8, high = 9
归并结果: 2 18 20 34 12 32 6 16 5 8 1
第2趟归并:
recs[0,1]和recs[2,3]归并: low = 0, mid = 1, high = 3
recs[4,5]和recs[6,7]归并: low = 4, mid = 5, high = 7
*recs[8,9]和recs[10,10]归并: low = 8, mid = 9, high = 10
归并结果: 2 18 20 34 6 12 16 32 1 5 8
第3趟归并:
recs[0,3]和recs[4,7]归并: low = 0, mid = 3, high = 7
归并结果: 2 6 12 16 18 20 32 34 1 5 8
第4趟归并:
*recs[0,7]和recs[8,10]归并: low = 0, mid = 7, high = 10
归并结果: 1 2 5 6 8 12 16 18 20 32 34
排序后: 1 2 5 6 8 12 16 18 20 32 34