【C语言】【十大排序算法】基数排序

定义

基数排序(英语:Radix sort)是一种非比较型的排序算法,最早用于解决卡片排序的问题。基数排序将待排序的元素拆分为k个关键字,逐一对各个关键字排序后完成对所有元素的排序。

如果是从第1关键字到第k关键字顺序进行比较,则该基数排序称为MSD(Most Significant Digit first)基数排序;
如果是从第k关键字到第1关键字顺序进行比较,则该基数排序称为LSD(Least Significant Digit first)基数排序。

基数排序的基本思想

基数排序的基本思想是将待排序的数值按照个位、十位、百位等每一位上的数字进行排序。具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

k关键字元素的比较

下面用 a_i表示元素a的第i关键字。

假如元素有k个关键字,对于两个元素a和b,默认的比较方法是:

比较两个元素的第 1 关键字a_1b_1,如果a_1<b_1a < b,如果a_1 > b_1a > b,如果a_1 = b_1则进行下一步;
比较两个元素的第 2 关键字a_2b_2,如果a_2 < b_2a < b,如果a_2 > b_2a > b,如果a_2 = b_2则进行下一步;
……
比较两个元素的第 k 关键字a_kb_k,如果a_k < b_ka < b,如果a_k > b_ka > b,如果a_k = b_ka = b
例子:

  • 如果对自然数进行比较,将自然数按个位对齐后往高位补齐 0,则一个数字从左往右数第i位数就可以作为第i关键字;
  • 如果对字符串基于字典序进行比较,一个字符串从左往右数第i个字符就可以作为第i关键字;

MSD 基数排序

基于k关键字元素的比较方法,可以想到:先比较所有元素的第1关键字,就可以确定出各元素大致的大小关系;然后对具有相同第1关键字的元素,再比较它们的第2关键字……以此类推。
由于是从第1关键字到第k关键字顺序进行比较,由上述思想导出的排序算法称为MSD(Most Significant Digit first)基数排序。

MSD算法流程

将待排序的元素拆分为k个关键字,先对第1关键字进行稳定排序,然后对于每组具有相同关键字的元素再对第 2 关键字进行稳定排序(递归执行)……最后对于每组具有相同关键字的元素再对第k关键字进行稳定排序。

LSD 基数排序

MSD 基数排序从第1关键字到第k关键字顺序进行比较,为此需要借助递归或迭代来实现,时间常数还是较大,而且在比较自然数上还是略显不便。
而将递归的操作反过来:从第 k 关键字到第1关键字顺序进行比较,就可以得到 LSD(Least Significant Digit first)基数排序,不使用递归就可以完成的排序算法。

LSD算法流程

将待排序的元素拆分为k个关键字,然后先对 所有元素的第k关键字进行稳定排序,再对所有元素的第k-1关键字进行稳定排序,再对 所有元素的第k-2关键字进行稳定排序……最后对所有元素的第1关键字进行稳定排序,这样就完成了对整个待排序序列的稳定排序。

C语言实现案例:

该案例来自于百度百科:基数排序_百度百科 (baidu.com)

#include<stdio.h>
#include<math.h>

//查询数组中的最大数
int findMaxNum(const int* p, int n) {
    int max = 0;
    for (int i = 0; i < n; i++) {
        if (*(p + i) > max) {
            max = *(p + i);
        }
    }
    return max;
}

//获取数字的位数
int getLoopTimes(int num) {
    int count = 1;
    int temp = num / 10;
    while (temp != 0) {
        count++;
        temp = temp / 10;
    }
    return count;
}

//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
void sort2(int* p, int n, int loop) {
    //建立一组桶此处的20是预设的根据实际数情况修改
    int buckets[10][20] = {};
    //求桶的index的除数
    //如798个位桶index=(798/1)%10=8
    //十位桶index=(798/10)%10=9
    //百位桶index=(798/100)%10=7
    //tempNum为上式中的1、10、100
    int tempNum = (int)pow(10, loop - 1);
    for (int i = 0; i < n; i++) {
        int row_index = (*(p + i) / tempNum) % 10;
        for (int j = 0; j < 20; j++) {
            if (buckets[row_index][j] == 0) {
                buckets[row_index][j] = *(p + i);
                break;
            }
        }
    }
    //将桶中的数,倒回到原有数组中
    int k = 0;
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 20; j++) {
            if (buckets[i][j] != 0) {
                *(p + k) = buckets[i][j];
                buckets[i][j] = 0;
                k++;
            }
        }
    }
}

//基数排序
void RadixSort(int* p, int n) {
    //获取数组中的最大数
    int maxNum = findMaxNum(p, n);
    //获取最大数的位数,次数也是再分配的次数。
    int loopTimes = getLoopTimes(maxNum);
    //对每一位进行桶分配
    for (int i = 1; i <= loopTimes; i++) {
        sort2(p, n, i);
    }
}


int main() {
    int a[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
    int* a_p = a;
    //计算数组长度
    int size = sizeof(a) / sizeof(int);
    printf("排序前:");
    for (int i = 0; i < size; i++) {
        printf("%d ", a[i]);
    }
    //基数排序
    RadixSort(a_p, size);
    //打印排序后结果
    printf("\n排序后:");
    for (int i = 0; i < size; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}

执行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

碧波bibo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值