C语言-数据结构 归并排序(递归版+非递归版)

        归并排序的思想是比较简单的,先说递归方式,先要把排序的数列一分为二一直分(整个过程采用递归),直到每个子序列中都只有一个元素的时候,然后开始两两合并,合并的过程调下大小顺序,合并到只有一个子序列排序完成。非递归方式,一开始就把排序的数列看成一个一个分开的,直接开始进行两两合并。在合并实现代码就是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; // 程序结束
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值