定义
基数排序(英语:Radix sort)是一种非比较型的排序算法,最早用于解决卡片排序的问题。基数排序将待排序的元素拆分为k个关键字,逐一对各个关键字排序后完成对所有元素的排序。
如果是从第1关键字到第k关键字顺序进行比较,则该基数排序称为MSD(Most Significant Digit first)基数排序;
如果是从第k关键字到第1关键字顺序进行比较,则该基数排序称为LSD(Least Significant Digit first)基数排序。
基数排序的基本思想
基数排序的基本思想是将待排序的数值按照个位、十位、百位等每一位上的数字进行排序。具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
k关键字元素的比较
下面用 表示元素a的第i关键字。
假如元素有k个关键字,对于两个元素a和b,默认的比较方法是:
比较两个元素的第 1 关键字和,如果则,如果则,如果则进行下一步;
比较两个元素的第 2 关键字和,如果则,如果则,如果则进行下一步;
……
比较两个元素的第 k 关键字和,如果则,如果则,如果则。
例子:
- 如果对自然数进行比较,将自然数按个位对齐后往高位补齐 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;
}