算法原理
基数 排序 是 箱 排序 的 改进 和 推广。 箱 排序 也称 桶 排序( Bucket Sort), 其 基本 思想 是: 设置 若干个 箱子, 依次 扫描 待 排序 的 记录 R[ 0], R[ 1],…, R[ n- 1], 把 关键字 等于 k 的 记录 全都 装入 到 第 k 个 箱子 里( 分配), 然后 按序 号 依次 将 各 非 空的 箱子 首尾 连接 起来( 收集)。 例如, 要将 一副 混 洗 的 52 张 扑克牌 按 点数 A< 2<…< J< Q< K 排序, 需 设置 13 个“ 箱子”, 排序 时 依次 将 每张 牌 按 点数 放入 相应 的 箱子 里, 然后 依次 将 这些 箱子 首尾相接, 就得 到了 按 点数 递增 顺序 排列 的 一副 牌。 基数 排序 是 基于 多 关键字 的, 什么 是 多 关键字 呢? 如果 文件 中 任何 一个 记录 R[ i] 的 关键字 都由 d 个 分量 构成, 而且 这 d 个 分量 中 每个 分量 都是 一个 独立 的 关键字, 则 文件 是 多 关键字 的( 比如 扑克牌 有 两个 关键字: 点数 和 花色)。 通常 实现 多 关键字 排序 有两 种 方法: 最高 位 优先( Most Significant Digit first, MSD); 最低 位 优先( Least Significant Digit first, LSD)。 基数 排序 是 典型的 LSD 排序 方法, 其 基本 思想 是: 从低 位 到 高位 依次 对数 据 进行 箱 排序。 在 d 趟 箱 排序 中, 所需 的
箱子 数 就是 基数 rd( 可能 的 取值 个数), 这就 是“ 基数 排序” 名称 的 由来。 比如, 对于 值 范围 为 10 ~ 99 的 整数 序列: 45, 13, 58, 64, 29, 74, 39, 18, 使用 基数 排序 需要 10 个 箱子( 从 0 ~ 9 标号) 进行 分配 和 收集。 我们 如果 把 每一 个数 看成 由 两个 关键字 构成( 个 位数 和 十 位数), 那么 可以 对 它们 进行 两次 分配 和 收集( 分别 对于 个位 和 十位), 具体 步骤 如下。 (1) 对 序列 的 各个 元素 按 个位 进行 顺序 装箱, 即 45 装入 5 号 箱, 13 装入 3 号 箱, 58 和 18 装入 8 号 箱, 64 和 74 装入 4 号 箱, 29 和 39 装入 9 号 箱。 (2) 从 0 到 9 号 箱 顺序 依次 收集 到 原 序列, 即 3 号 箱 的 13, 4 号 箱 的 64 和 74, 5 号 箱 的 45, 8 号 箱 的 58 和 18, 9 号 箱 的 29 和 39 被 依次 收集。 序列 变为 13, 64, 74, 45, 58, 18, 29, 39。 (3) 对 序列 的 各个 元素 按 十位 进行 顺序 装箱, 即 13 和 18 装入 1 号 箱, 29 装入 2 号 箱, 30 装入 3 号 箱, 45 装入 4 号 箱, 58 装入 5 号 箱, 64 装入 6 号 箱, 74 装入 7 号 箱。 (4) 再次 从 0 到 9 号 箱 顺序 收集 到 原 序列, 序列 变为 13, 18, 29, 30, 45, 58, 64, 74。 此时 完成 基数 排序。 对于 一个 两位 数 来说, 其 十位 数 当然 比 个位 数 关键。 因此 使用 LSD 时, 先 对 个位 数 开始 分配 和 收集。
算法草稿
代码实现
#include <stdio.h>
#include <stdlib.h>
#define SUCCESS 0
#define PARAM_ERR -1
#define BUCKET_NUM 10 /*桶个数*/
#define BUCKET_DEEP 100 /*捅深度*/
/*
* 每个桶的头,包含桶元素数组和桶中有效元素的个数
* 桶元素这里假设只有100个元素,这个限制可以通过链表处理来突破
*/
typedef struct bucket{
int elem[BUCKET_DEEP]; /*桶元素*/
int count; /* 桶元素中有效的个数*/
}st_bucket;
/*
* 正常桶深应该用链表,这里进行整数的排序,关键字10个,为 0~9
* 默认可以排序100个数,可以通过将桶元素变为链表来突破这个限制
*/
st_bucket g_buckets[BUCKET_NUM];
/*
* 初始化 buckets,将所有的桶的有效count设置为0,表示没有有效元素
*/
void InitBuckets(void){
int i, j;
for(i = 0; i < BUCKET_NUM; i++){
g_buckets[i].count = 0;
}
return;
}
/*
* 返回array中最大的数
*/
int getMaxNum(int * array, int size){
int i = 0;
int max = 0;
max = array[0];
for(i = 0; i < size; i++){
if(array[i] > max){
max = array[i];
}
}
return max;
}
/* 根据num的位数,取得排序的轮数*/
int getRnd(int num){
int rnd = 0;
do {
rnd++;
num = num / 10;
} while (0 != num);
return rnd;
}
int Scatter(int * array, int size, int digit){
if(NULL == array){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
int i = 0, num = 0, d = 0;
int key = 0; /*桶关键字*/
for(i = 0; i < size; i++){
/* 取得桶关键字 */
num = array[i];
for (d = 1; d <= digit; d++){
key = num % 10;
num = num / 10;
}
/*将数字插入排序桶中*/
g_buckets[key].elem[g_buckets[key].count++] = array[i];
}
return SUCCESS;
}
int gather(int * array, int size) {
if(NULL == array){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
int i = 0, j = 0, k = 0;
int count = 0;
/*升序*/
for(i = 0; i < BUCKET_NUM; i++){
count = g_buckets[i].count;
for(j = 0; j < count; j++){
array[k++] = g_buckets[i].elem[j];
}
}
return SUCCESS;
}
int BucketSort(int * array, int size){
if(NULL == array){
printf("%s para error\n", __func__);
return PARAM_ERR;
}
int digit = 0;
int max = 0;
int rnd = 0;
#ifdef DEBUG
int i = 0, j = 0, k = 0, c = 0;
#endif
/*初始化桶*/
/* 取得最大的数字 */
max = getMaxNum(array, size);
/* 取得排序的轮数 */
rnd = getRnd(max);
for(digit = 1; digit <= rnd; digit++){
/* 重新初始化桶*/
InitBuckets();
/* 分散到桶中 */
Scatter(array, size, digit);
/* 从桶中聚合 */
gather(array, size);
#ifdef DEBUG
printf("============= Digit %d =========== \n", digit);
printf(" Scattered Bucket:\n");
/*有序区域*/
for(i =0; i < BUCKET_NUM; i++){
printf("\t Bucket[%d]: ", i);
c = g_buckets[i].count;
for(j = 0; j < c; j++){
printf(" %d ", g_buckets[i].elem[j]);
}
printf("\n");
}
printf(" --------------------------------- \n ");
/*无序区域*/
printf("Gattered Array: \n");
printf("\t");
for(k =0; k < size; k++){
printf(" %d ", array[k]);
}
printf("\n");
printf("================================== \n\n");
#endif
}
return SUCCESS;
}
int main(int argc, char ** argv){
int array[10] = {22,32,19,53,0,47,29,116,4,6};
int i = 0;
printf("Before sort: \n");
for(i = 0; i < 10; i++){
printf(" %d ", array[i]);
}
printf("\n");
BucketSort(array, 10);
printf("After sort: \n");
for(i = 0; i < 10; i++){
printf(" %d ", array[i]);
}
printf("\n");
return 0;
}
调试编译
gcc BucketSort.c -DDEBUG
调试输出
Before sort:
22 32 19 53 0 47 29 116 4 6
============= Digit 1 ===========
Scattered Bucket:
Bucket[0]: 0
Bucket[1]:
Bucket[2]: 22 32
Bucket[3]: 53
Bucket[4]: 4
Bucket[5]:
Bucket[6]: 116 6
Bucket[7]: 47
Bucket[8]:
Bucket[9]: 19 29
---------------------------------
Gattered Array:
0 22 32 53 4 116 6 47 19 29
==================================
============= Digit 2 ===========
Scattered Bucket:
Bucket[0]: 0 4 6
Bucket[1]: 116 19
Bucket[2]: 22 29
Bucket[3]: 32
Bucket[4]: 47
Bucket[5]: 53
Bucket[6]:
Bucket[7]:
Bucket[8]:
Bucket[9]:
---------------------------------
Gattered Array:
0 4 6 116 19 22 29 32 47 53
==================================
============= Digit 3 ===========
Scattered Bucket:
Bucket[0]: 0 4 6 19 22 29 32 47 53
Bucket[1]: 116
Bucket[2]:
Bucket[3]:
Bucket[4]:
Bucket[5]:
Bucket[6]:
Bucket[7]:
Bucket[8]:
Bucket[9]:
---------------------------------
Gattered Array:
0 4 6 19 22 29 32 47 53 116
==================================
After sort:
0 4 6 19 22 29 32 47 53 116