基数排序
如果要问排序哪家快,那一定是基数排序,在线性时间内即可完成排序。在了解基数排序之前,我们先了解一下基础排序的低配版-桶排序。桶排序是一种典型的空间换时间的排序方法,小规模排序(比如1-100分数排序)是可以靠桶排序完成的,所以我们先介绍桶排序,然后看看桶排序的缺点,最后介绍基数排序
桶排序
桶排序很简单,首先按照待排序数组中最大的数申请多少个桶。比如排序5 1 2 5 3
需要申请5个桶,然后从第一个元素开始,依次将元素丢入相应的桶中,最后只需要循环检查一点桶中的元素依次输出就实现排序了,下图表示桶排序过程
按照上图的流程,桶排序代码实现
#include<stdio.h>
void bucketSort(int arr[], int length);
void main(){
int arr[] = {5, 1, 2, 5, 3};
bucketSort(arr, 5);
int k;
for(k=0; k<5; k++){
printf("%d ", arr[k]);
}
}
void bucketSort(int arr[], int length){
//申请6个桶
int bucket[5];
int i;
for(i=0; i<6; i++){
bucket[i] = 0;//初始化桶中元素数量为0
}
//将元素放入桶中
int j;
for(j=0; j<length; j++){
bucket[arr[j]]++;
}
//处理结构
int k;//桶的编号
int g=0;//存放排序后数组编号
for(k=0; k<6; k++){
while(bucket[k] > 0){
arr[g] = k;
g++;
bucket[k] --;
}
}
}
桶排序是能在线性时间内完成的,比插入排序,快速排序都要快。桶排序适合一定规模的数组排序(比如分数,年龄等)。但是一般不是这种情况,比如要排序2 1 999999
。这个时候如果按照桶排序的思路,需要申请999999个桶,显然,空间浪费太大。所以接下来介绍基数排序来解决这个问题
基数排序
基数排序按照十进制的基数只有10个桶,从0-9编号。核心思想是先比较个位数,再比较十位数,再比较百位数...。所以每次比较其中一个数字,10个桶足够。比如要排序
64,8,216,512,27,729,0,1,343,125
下面就按照基数排序次位优先(先排低位)来进行
第一步:按照个位数分别入桶,比如64落在4号桶,8落在8号桶,216落在6号桶
第二步:在第一步的基础上,按照十位数依次入桶,0还是0号桶,1也是0号桶,512是1号桶。这个时候可能一个桶上有多个数字出现,可以用链表连接起来。
第三步:在第二部的基础上,按照百位数依次入桶,比如,0,1,8还是在0号桶,512在5号桶,216在2号桶。
最后,从第0个桶到第9个桶一次按照顺序输出这些元素,就是有序的:
0,1,8,27,64,125,216,512,719
基数排序的实现
由于每个桶内可能有多个元素,我们采用链表来存储元素信息。每个元素都是一个节点,这个节点的结构体如下
struct Node {
int key;
struct Node *next;
};
比如我们排序1 33 2 13 55
,分三步完成我们的最低位排序过程
第一步,将数组放入链表中
第二步,将第一步链表中的元素按照最低位放入桶中,注意,桶也是一个链表,刚开始初始化的时候是空链表(head=tail=null),放入桶中之后,元素被分成了三部分,橙色链表,红色链表,蓝色链表。
第三步,按照桶的顺序(0-9)将链表再次连接起来,这里即把橙色,红色,蓝色链表连接起来。
然后生成新的链表之后,又可以回到第一步,这不过这一回是按照十位数进行排序了。我们用C语言代码实现上述步骤。
#include<stdio.h>
#include <stdlib.h>
//定义最大位数为4 基数是十进制
#define MaxDigit 4
#define Radix 10
//定义元素节点
struct Node {
int key;
struct Node * next;
};
typedef struct Node *PtrToNode;
// 桶节点,头和尾组成, 每个桶放一个链表
struct BucketNode {
PtrToNode head;
PtrToNode tail;
};
void bucketSort(int arr[], int length);
int getDigit(int, int);
int main(){
int arr[] = {1, 15, 3, 25, 55, 7};
bucketSort(arr, 6);
int i;
for(i=0; i<6; i++){
printf("%d ", arr[i]);
}
}
//获取相应位数的基数
int getDigit ( int x, int d )
{
int di, i;
for (i = 1; i<=d; i++) {
di = x % Radix;
x /= Radix;
}
return di;
}
//基数排序实现
void bucketSort(int arr[], int length){
//第一步,把数组串成链表
PtrToNode List = NULL;
PtrToNode tmp;
int i;
for(i=0; i<length; i++){
tmp = (PtrToNode)malloc(sizeof(struct Node));
tmp->key = arr[i];
tmp->next = List;
List = tmp;
}
//初始化桶,0-9号桶,每个都先初始化空链表
struct BucketNode BList[Radix];
int j;
for(j=0; j<Radix; j++){
BList[j].head = NULL;
BList[j].tail = NULL;
}
int k;
for(k = 1; k < MaxDigit; k++){
PtrToNode p;
p = List;
//第二步,遍历数组串成的队列,按照最末尾数组入桶中的队列
while(p){
//取出一个元素放在tmp中
tmp = p;
p = p->next;
tmp->next = NULL;
int di = getDigit(tmp->key, k);
if(!BList[di].head){
BList[di].head = BList[di].tail = tmp;
}else{
BList[di].tail->next = tmp;
BList[di].tail = tmp;
}
}
//第三步,检查每个桶中链表是否为空,将不为空的链表再次首尾连接起来
List = NULL;
int t;
for(t=Radix -1; t >= 0; t--){
if(BList[t].head){
BList[t].tail->next = List;
List = BList[t].head;
BList[t].head = BList[t].tail = NULL; /* 清空桶 */
}
}
}
/* 将List倒入arr[]并释放空间 */
for (i=0; i<length; i++) {
tmp = List;
List = List->next;
arr[i] = tmp->key;
free(tmp);
}
}