归并排序的思想是比较简单的,先说递归方式,先要把排序的数列一分为二一直分(整个过程采用递归),直到每个子序列中都只有一个元素的时候,然后开始两两合并,合并的过程调下大小顺序,合并到只有一个子序列排序完成。非递归方式,一开始就把排序的数列看成一个一个分开的,直接开始进行两两合并。在合并实现代码就是Merge函数稍微有点小麻烦,注意Merge函数在递归与非递归中都是一样的实现,需要我们仔细一点点来理解下标的移动过程。在希尔和堆排序里0位置我们用于临时存取数据,在归并排序中0位置是占位作用,数组0位置不存储任何数据,你会想说为啥0不能存,当然是可以存数据的,只不过我们后面用下标1-length来操作比较容易理解计算下标。归并排序推荐使用循环实现,实用性好效率更高也好理解。其中部分位置的计算可能有点小麻烦,初学为了方便理解,下标就不使用i、j这种字母表示,第一遍手动计算需要耐心。
递归版具体代码如下:
#include<stdio.h>
#define MAXSIZE 9 // 定义最大数组大小为9
// 合并函数,将两个已排序的子数组合并成一个已排序的数组
void Merge(int OriginalArray[], int TargetArray[], int left, int mid, int length) {
int leftIndex = left; // 左子数组的起始索引
int rightindex = mid + 1; // 右子数组的起始索引
int TargetArrayIndex = left; // 目标数组的当前索引
int l;
// 合并两个已排序的子数组
for (; leftIndex <= mid && rightindex <= length; TargetArrayIndex++) {
if (OriginalArray[leftIndex] < OriginalArray[rightindex]) {
TargetArray[TargetArrayIndex] = OriginalArray[leftIndex++]; // 从左子数组中取出元素
}
else {
TargetArray[TargetArrayIndex] = OriginalArray[rightindex++]; // 从右子数组中取出元素
}
}
// 如果左子数组还有剩余元素,则将其复制到目标数组中
if (leftIndex <= mid) {
for (l = 0; l <= mid - leftIndex; l++) {
TargetArray[TargetArrayIndex + l] = OriginalArray[leftIndex + l]; // 复制剩余左子数组元素
}
}
// 如果右子数组还有剩余元素,则将其复制到目标数组中
if (rightindex <= length) {
for (l = 0; l <= length - rightindex; l++) {
TargetArray[TargetArrayIndex + l] = OriginalArray[rightindex + l]; // 复制剩余右子数组元素
}
}
}
// 归并排序函数,通过递归分治法对数组进行排序
void MSort(int OriginalArray[], int TargetArray1[], int left, int right) {
int mid; // 中间索引
int TargetArray2[MAXSIZE + 1]; // 临时目标数组,用于存放合并结果
// 基本情况:如果只有一个元素,直接赋值
if (left == right) {
TargetArray1[left] = OriginalArray[left]; // 只有一个元素,直接拷贝
}
else {
mid = (left + right) / 2; // 计算中间索引
MSort(OriginalArray, TargetArray2, left, mid); // 递归排序左半部分
MSort(OriginalArray, TargetArray2, mid + 1, right); // 递归排序右半部分
Merge(TargetArray2, TargetArray1, left, mid, right); // 合并两部分
}
}
// 归并排序的入口函数
void MergeSort(int OriginalArray[], int length) {
MSort(OriginalArray, OriginalArray, 1, length); // 调用 MSort 函数进行排序
}
int main() {
int OriginalArray[10] = { 0, 50, 10, 90, 30, 70, 40, 80, 60, 20 }; // 原始数组,注意第一个元素为0占位,保证后面数据下标索引为1-length,不存取数据
int length = 9; // 数组长度
MergeSort(OriginalArray, length); // 调用归并排序
// 打印排序后的数组
for (int i = 1; i <= 9; i++) {
printf("%d->", OriginalArray[i]); // 输出排序结果
}
return 0; // 程序结束
}
运行结果:
下面是非递归版:
#include<stdio.h>
#include<stdlib.h>
// 合并函数:将两个已排序的子数组合并到目标数组中
void Merge(int OriginalArray[], int TargetArray[], int left, int mid, int length) {
int leftIndex = left; // 左子数组的起始索引
int rightindex = mid + 1; // 右子数组的起始索引
int TargetArrayIndex = left; // 目标数组的当前索引
int l;
// 合并两个已排序的子数组
for (; leftIndex <= mid && rightindex <= length; TargetArrayIndex++) {
if (OriginalArray[leftIndex] < OriginalArray[rightindex]) {
TargetArray[TargetArrayIndex] = OriginalArray[leftIndex++]; // 从左子数组中取出元素
}
else {
TargetArray[TargetArrayIndex] = OriginalArray[rightindex++]; // 从右子数组中取出元素
}
}
// 如果左子数组还有剩余元素,则将其复制到目标数组中
if (leftIndex <= mid) {
for (l = 0; l <= mid - leftIndex; l++) {
TargetArray[TargetArrayIndex + l] = OriginalArray[leftIndex + l]; // 复制剩余左子数组元素
}
}
// 如果右子数组还有剩余元素,则将其复制到目标数组中
if (rightindex <= length) {
for (l = 0; l <= length - rightindex; l++) {
TargetArray[TargetArrayIndex + l] = OriginalArray[rightindex + l]; // 复制剩余右子数组元素
}
}
}
// 合并过程:对指定长度的子数组进行合并
void MergePass(int SR[], int TR[], int s, int n) {
int i = 1; // 当前子数组的起始索引
int j;
// 合并相邻的两个子数组,直到不能再合并为止
while (i < n - 2 * s + 1) {
Merge(SR, TR, i, i + s - 1, i + 2 * s - 1); // 合并两个子数组
i = i + 2 * s; // 移动到下一个待合并的子数组
}
// 如果还有一个子数组剩余,合并它
if (i < n - s + 1) {
Merge(SR, TR, i, i + s - 1, n); // 合并最后一个子数组
}
else {
// 复制剩余元素到目标数组中
for (j = i; j <= n; j++) {
TR[j] = SR[j]; // 直接拷贝剩余元素
}
}
}
// 归并排序主函数:实现归并排序算法
void MergeSort(int Array[], int length) {
int* TR = (int*)malloc(length * sizeof(int)); // 动态分配目标数组的内存
int k = 1; // 初始步长
// 进行多次合并操作,逐步增大步长
while (k < length) {
MergePass(Array, TR, k, length); // 合并原始数组到目标数组
k = 2 * k; // 步长翻倍
MergePass(TR, Array, k, length); // 合并目标数组回到原始数组
}
}
int main() {
int a[10] = { 0, 50, 10, 90, 30, 70, 40, 80, 60, 20 }; // 原始数组,注意第一个元素为0占位
int length = 9; // 数组实际长度
MergeSort(a, length); // 调用归并排序进行排序
// 打印排序后的数组
for (int i = 1; i <= 9; i++) {
printf("%d->", a[i]); // 输出排序结果
}
return 0; // 程序结束
}
运行结果: