所谓基数排序,是指根据数据的基数(如10进制基数为10)建桶(10进制基数为10建立0~9十个桶)
基数排序(Radix Sort)是桶排序的扩展
- 链接:桶排序
- 计数排序时如果数据区间过大,将会造成构造数组过大浪费空间
- 桶排序时如果数据的分布不均匀将会造成数据集中到一个桶中造成麻烦
算法描述
-
以排序数据的进制为个数,建立其基数个桶
-
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。
-
按照“次位优先”(Least Significant Digit)从个位开始依次进桶(类似对个位进行计数排序)
-
使用一个链表按照从小桶到大桶将数据依次串起(这样得到的就是按照个位排好序的序列)
-
然后按照十位从链表头开始将元素依次进桶
- 由于在链表上元素已经为个位有序(按照个位上的值排列),即个位小的在前面个位大的在后面
- 而后又从链表头开始依次进桶,此时十位同值时,个位小的先进桶个位大的后进桶,出桶时只要保证从桶的链表头开始出桶即可保证后续排序依然有序
-
这样从最低位排序一直到最高位依次进行排序完成以后, 数列就变成一个有序序列。
算法实现
/* 基数排序 - 次位优先 */
/* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */
#define MaxDigit 4
#define Radix 10
/* 桶元素结点 */
typedef struct Node *PtrToNode;
struct Node {
int key;
PtrToNode next;
};
/* 桶头结点 */
struct HeadNode {
PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];
int GetDigit ( int X, int D )
{ /* 默认次位D=1, 主位D<=MaxDigit */
int d, i;
for (i=1; i<=D; i++) {
d = X % Radix;
X /= Radix;
}
return d;
}
void LSDRadixSort( ElementType A[], int N )
{ /* 基数排序 - 次位优先 */
int D, Di, i;
Bucket B;
PtrToNode tmp, p, List = NULL;
for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */
B[i].head = B[i].tail = NULL;
for (i=0; i<N; i++) { /* 将原始序列逆序存入初始链表List */
tmp = (PtrToNode)malloc(sizeof(struct Node));
tmp->key = A[i];
tmp->next = List;
List = tmp;
}
/* 下面开始排序 */
for (D=1; D<=MaxDigit; D++) { /* 对数据的每一位循环处理 */
/* 下面是分配的过程 */
p = List;
while (p) {
Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
/* 从List中摘除 */
tmp = p; p = p->next;
/* 插入B[Di]号桶尾 */
tmp->next = NULL;
if (B[Di].head == NULL)
B[Di].head = B[Di].tail = tmp;
else {
B[Di].tail->next = tmp;
B[Di].tail = tmp;
}
}
/* 下面是收集的过程 */
List = NULL;
for (Di=Radix-1; Di>=0; Di--) { /* 将每个桶的元素顺序收集入List */
if (B[Di].head) { /* 如果桶不为空 */
/* 整桶插入List表头 */
B[Di].tail->next = List;
List = B[Di].head;
B[Di].head = B[Di].tail = NULL; /* 清空桶 */
}
}
}
/* 将List倒入A[]并释放空间 */
for (i=0; i<N; i++) {
tmp = List;
List = List->next;
A[i] = tmp->key;
free(tmp);
}
}