归并排序和基数排序是两种重要的排序算法,各自基于不同的思想实现高效、稳定的排序

归并排序和基数排序是两种重要的排序算法,各自基于不同的思想实现高效、稳定的排序。


一、归并排序(Merge Sort)

核心逻辑:分治法

  • 分解:将待排序数组从中间分割为两个子数组,递归地对左右两部分进行排序。
  • 合并:使用 Merge 函数将两个已排序的子数组合并成一个有序数组。
关键函数说明:
void MSort(int data[], int s, int t) {
    if (s < t) {
        int m = (s + t) / 2;
        MSort(data, s, m);      // 排序左半部分
        MSort(data, m+1, t);    // 排序右半部分
        Merge(data, s, m, t);   // 合并两个有序段
    }
}
  • Merge(data, s, m, t) 实现:
    • 创建临时数组暂存原数组片段
    • 使用双指针分别遍历左半 [s, m] 和右半 [m+1, t]
    • 按升序将较小元素复制到结果中
    • 复制剩余元素
    • 将临时数组内容拷贝回原数组
入口函数:
void MergeSort(int data[], int n) {
    MSort(data, 0, n-1);
}
  • 时间复杂度:O(n log n),无论最好、最坏、平均情况
  • 空间复杂度:O(n) —— 需要额外的临时数组用于合并
  • 稳定性:稳定(相等元素相对位置不变)
  • 适用场景:要求稳定排序或链表排序时表现优异

二、基数排序(Radix Sort)

基本思想:按位排序 + 分配收集

将整数视为由多个“位”组成的数字串(如十进制下个、十、百位),从最低位开始逐位排序。

排序步骤:
  1. 设定 r 个队列(例如 r=10 对应数字 0~9)
  2. 从最低有效位(LSD)开始处理每一位:
    • 分配:根据当前位上的数值,把每个数放入对应编号的队列中
    • 收集:按队列 0→r-1 的顺序取出所有元素,形成新的序列
  3. 重复上述过程直到最高位处理完毕

示例:排序 [170, 45, 75, 90, 2, 802, 24]

第一次(个位)后:[170, 90, 2, 802, 24, 45, 75]
第二次(十位)后:[2, 802, 24, 45, 75, 170, 90]
第三次(百位)后:[2, 24, 45, 75, 90, 170, 802]

特性分析:
  • 稳定性:稳定 —— 队列先进先出保证相同关键字顺序不乱
  • 时间复杂度:O(d(n + r))
    • d:最大数的位数(如最大为999,则d=3)
    • n:元素个数
    • r:基数(如十进制r=10)
  • 空间复杂度:O(r + n)
    • 需要 r 个队列头尾指针(链式队列)
    • 每个元素需存储指针(若用链表结构)
应用场景:
  • 整数排序(尤其是固定位宽的键值,如电话号码、身份证号)
  • 外部排序中的辅助手段
  • 当 d 较小且 r 不大时效率很高,可达到线性时间趋势

总结对比:

特性归并排序基数排序
排序依据比较元素大小按位分配
是否比较排序
时间复杂度O(n log n)O(d(n + r))
空间复杂度O(n)O(n + r)
稳定性稳定稳定
适用数据类型可比较任意对象数字、字符串等可分解关键字
最佳应用场景要求稳定、通用性强数据范围集中、位数少

归并排序中的 Merge 函数是核心操作,用于将两个已排序的子数组合并为一个有序数组。以下是完整的 C 语言代码实现,包含详细注释:

#include <stdio.h>
#include <stdlib.h>

// 合并函数:将 data[s..m] 和 data[m+1..t] 两个有序子数组合并为有序数组
void Merge(int data[], int s, int m, int t) {
    int n1 = m - s + 1;  // 左半部分长度
    int n2 = t - m;      // 右半部分长度

    // 动态分配临时数组存储左右两部分
    int *left = (int *)malloc(n1 * sizeof(int));
    int *right = (int *)malloc(n2 * sizeof(int));

    if (!left || !right) {
        printf("内存分配失败\n");
        exit(1);
    }

    // 复制数据到临时数组
    for (int i = 0; i < n1; i++)
        left[i] = data[s + i];
    for (int j = 0; j < n2; j++)
        right[j] = data[m + 1 + j];

    // 归并过程:双指针法合并
    int i = 0, j = 0, k = s;

    while (i < n1 && j < n2) {
        if (left[i] <= right[j]) {  // 使用 <= 保证稳定性
            data[k++] = left[i++];
        } else {
            data[k++] = right[j++];
        }
    }

    // 复制剩余元素
    while (i < n1)
        data[k++] = left[i++];
    while (j < n2)
        data[k++] = right[j++];

    // 释放临时内存
    free(left);
    free(right);
}

// 递归分治排序
void MSort(int data[], int s, int t) {
    if (s < t) {
        int m = s + (t - s) / 2;  // 防止整数溢出
        MSort(data, s, m);
        MSort(data, m + 1, t);
        Merge(data, s, m, t);
    }
}

// 入口函数:对长度为 n 的数组进行归并排序
void MergeSort(int data[], int n) {
    MSort(data, 0, n - 1);
}

// 示例测试程序
int main() {
    int arr[] = {38, 27, 43, 3, 9, 82, 10};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前: ");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    printf("\n");

    MergeSort(arr, n);

    printf("排序后: ");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    printf("\n");

    return 0;
}

输出结果:

排序前: 38 27 43 3 9 82 10 
排序后: 3 9 10 27 38 43 82 

关键点说明:

  • 临时数组:使用 malloc 分配空间暂存左右子数组,避免覆盖。
  • 稳定性保障:合并时使用 <= 判断,确保相等元素保持原有顺序。
  • 边界处理:正确计算索引范围 [s..m][m+1..t]
  • 资源管理:及时释放动态分配的内存。

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值