基数排序
写在前头(〃‘▽’〃)
基数排序时桶排序的一种推广(桶排序详解),它所考虑的待排记录包含不止一个关键字。例如对一副牌进行整理,可将每张牌看做一个记录,包含两个关键字:花色、面值。一副理顺的牌是按如下顺序进行排放的:
可见一个有序结果是先按花色划分成四大块,每一块中有按面值大小排序。这是“花色”就是一张牌的“最主位关键字”,而“面值”是“最次位关键字”。
对于一般有K个关键字的情况,基数排序通常有两种方法:
- 主位优先法(MOst Significant Digit Sort,简称MST)
- 次位优先法(Least Significant Digit Sort,简称LST)
从上述例子可见,两种方法具有不同的特点:
- 主位优先法基本是分而治之的思路,将序列分割成子序列后,分别排序再合并结果;
- 次位优先法是将“排序”过程分解成了“分配”和“收集”这两个相对简单的步骤,并不需要分割子序列并排序,因此一般情况下次位优先法的效率更高一些。
更棒的栗子٩(๑>◡<๑)۶
- 对于待排序列A[] = {83, 8, 269, 505, 184, 589, 930, 63, 109, 278},先倒着存进一个list中
- 以数的位作为基数,利用次位优先法,第一趟按照个位搬来10个Buckets,相应元素丢桶;第二趟按照十位搬来10个Buckets,相应元素丢桶;第三趟按照百位搬来10个Buckets,相应元素丢桶,丢丢丢~~
- 只需要收集最后一趟的排序结果,就得到最终的排序结果啦(。◕ˇ∀ˇ◕)
可食用的代码(参考)ヾ(๑╹◡╹)ノ"
#include <stdio.h>
#include <stdlib.h>
/*-----------LSD次位优先----------*/
/*假设元素最多有MaxDigit个关键字,每趟桶的个数相同*/
#define BucketSize 10 //每趟桶的个数
#define MaxDigit 4
typedef int ElementType;
typedef struct Node *PNode;
struct Node{//桶元素结点
ElementType Data;
PNode next;
};
struct HNode{//桶头结点
PNode head, last;
};
typedef struct HNode Bucket[BucketSize];
void PrintBucket(PNode Blist);//打印当前桶内的元素
int GetDigit(int X, int D);//获得元素X所对应的桶的下标
void RadixSort(ElementType A[], int N);//核心排序函数
int GetDigit(int X, int D)
//获得桶的下标
{
int index;
for (int i = 1; i <= D; i++){
index = X % BucketSize;
X /= BucketSize;
}
return index;
}
void RadixSort(ElementType A[], int N)
//统一函数接口
{
Bucket B;
int i, j, k, index;
PNode list = NULL;
//将数组A[]倒入链表list中,逆着倒
for (i = 0; i < N; i++){
PNode temp = (PNode)malloc(sizeof(struct Node));
temp->Data = A[i];
temp->next = list;
list = temp;//list即为序列链表的头结点
}
for (i = 1; i < MaxDigit; i++){
//初始化桶头结点
for (j = 0; j < BucketSize; j++)
B[j].head = B[j].last = NULL;
//待排序列在链表list中,头结点为list
//下面开始丢桶
while (list){
index = GetDigit(list->Data, i);//获得A[j]对应的桶下标
//不能破坏原序列链表list,因此需要过渡结点tmp
PNode tmp = list;
list = list->next;
tmp->next = NULL;
if (B[index].head == NULL)
B[index].head = B[index].last = tmp;
else{
B[index].last->next = tmp;
B[index].last = tmp;
}
}
//打印此时每个桶内的元素
printf("---------pass_%d-------\n", i);
for (k = 0; k < BucketSize; k++){
printf("Bucket[%d] : ", k);
PrintBucket(B[k].head);
}
//下面开始收集,即将每条桶链表按照顺序连接起来,倒回list中
//同样需要逆着倒
list = NULL;
for (k = BucketSize - 1; k >= 0; k--){
if (B[k].head){
B[k].last->next = list;
list = B[k].head;
B[k].head = B[k].last = NULL;//清空桶
}
}
//打印当前序列list
PNode tmp = list;
while (tmp){
printf("%d ",tmp->Data);
tmp = tmp->next;
}
printf("\n");
}
//将list倒回A[]中并释放空间
for (int i = 0; i< N; i++){
PNode tmp = list;
list = list->next;
A[i] = tmp->Data;
free(tmp);
}
}
void PrintBucket(PNode Blist)
//打印当前桶内元素
{
if (Blist == NULL);
else{
while (Blist){
printf("%d ", Blist->Data);
Blist = Blist->next;
}
}
printf("\n");
}
int main(int argc, char const *argv[])
{
ElementType A[] = {83, 8, 269, 505, 184, 589, 930, 63, 109, 278};
RadixSort(A, 10);
printf("------result-------\n");
for (int i = 0; i < 10; i++){
if (i == 9)
printf("%d\n",A[i]);
else
printf("%d ",A[i]);
}
system("pause");
return 0;
}
Reference
数据结构学习笔记排序 (快速,计数排序,表排序,桶排序,基数排序)
数据结构学习视频面试中的排序算法总结
基数排序与桶排序,计数排序【详解】