#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define SIZE 10
#define len 20
void show(int str[]);
void *Malloc(int size);
static int find_max(int *array, int length);
static int get_bit_size(int value);
static int get_num_in_bit(int value, int bit);
void show(int str[])
{
int i ;
for(i = 0;i < len ; i++)
{
printf(" %d",str[i]);
}
}
void *Malloc(int size)
{
void * result = (void *)malloc(size);
if(result == NULL)
{
printf("memery is full\n");
}
return result;
}
static int find_max(int *array, int length)
{
int max = 0;
int i = 0;
for(i = 1, max = array[0]; i < length; ++i){
if(array[i] > max){
max = array[i];
}
}
return max;
}
static int get_bit_size(int value)
{
int bit = 0;
// 100
while(value){
bit++;
value /= 10;
}
return bit;
}
static int get_num_in_bit(int value, int bit)
{
int num = 1;
while(bit-- > 1){
num *= 10;
}
return (value / num) % 10;
// 123456
//
// 100 = 1 * 10 * 10
//
// 1234 % 10 = 4
}
void base_sort(int *array, int length)
{
int *bucket[SIZE]; //指针的数组,
int i = 0;
int j = 0;
int k = 0;
int max_value = 0;
int max_bit = 0;
int bit = 0;
int bit_num = 0;
int count = 0;
int index = 0;
//1.对每一个桶申请空间
for(i = 0; i < SIZE; ++i){
//多申请一个int记录元素个数
bucket[i] = (int *)Malloc(sizeof(int) * (length + 1));
bucket[i][0] = 0;
}
//找出最大数,也就找到最大位
max_value = find_max(array, length);
max_bit = get_bit_size(max_value);
// 1.从个位到max_bit位依次进行hash和收集的操作
for(bit = 1; bit <= max_bit; ++bit){
for(i = 0; i < length; ++i){
//找到待排数据的第bit位的值,存放在对应的桶里
bit_num = get_num_in_bit(array[i], bit);
bucket[bit_num][0]++;
index = bucket[bit_num][0]; //新的元素存放的下标(刚好是元素个数)
bucket[bit_num][index] = array[i];
}
//从0到9号桶,依次把元素放在待排序列中
for( i = 0, j = 0; i < SIZE; ++i){
//取出每个桶内数据
count = bucket[i][0];
for(k = 1; k <= count; ++k){
array[j++] = bucket[i][k];
}
//把桶内元素个数清零,为下次hash做准备
bucket[i][0] = 0;
}
}
// 120 321 456 326 443 221 334 901 108 856 735 235
//
//
// 0
// 1 120
// 2
// 3 321 326
// 4 456 443
// 5
// 6
// 7
// 8
// 9
//
// 按位数从低到高依次排序
//
// (1)按个位排序
//
// 0 120
// 1 321 221 901
// 2
// 3 443
// 4 334
// 5 735 235
// 6 456 326 856
// 7
// 8 108
// 9
//
//120 321 221 901 443 334 735 235 456 326 856 108
//
//
//按十位排序
//
//
// 0 901 108
// 1
// 2 120 321 221 326
// 3 334 735 235
// 4 443
// 5 456 856
// 6
// 7
// 8
// 9
//
// 901 108 120 321 221 326 334 735 235 443 456 856
//
//对百位进行排序
//
//
// 0
// 1 108 120
// 2 221 235
// 3 321 326 334
// 4 443 456
// 5
// 6
// 7 735
// 8 856
// 9 901
//
// 108 120 221 235 321 326 334 443 456 735 856 901
}
int main()
{
int str[len];
int i ;
for(i = 0; i< len ;i++)
{
str[i] = rand()%100;
}
show(str);
printf("排序前:\n");
base_sort(str,len);
printf("排序后:\n");
show(str);
return 0;
}
41 67 34 0 69 24 78 58 62 64 5 45 81 27 61 91 95 42 27 36排
序前:
排序后:
0 5 24 27 27 34 36 41 42 45 58 61 62 64 67 69 78 81 91 95Pr
ess any key to continue
基数排序
单关键字可以使用这种方式。
比如字符串“abcd” “aesc” “dwsc” “rews”就可以把每个字符看成一个关键字。另外还有整数 425、321、235、432也可以每个位上的数字为一个关键字。
基数排序的思想就是将待排数据中的每组关键字依次进行桶分配。比如下面的待排序列:
278、109、063、930、589、184、505、269、008、083
我们将每个数值的个位,十位,百位分成三个关键字: 278 -> k1(个位)=8 ,k2(十位)=7 ,k3=(百位)=2。
然后从最低位个位开始(从最次关键字开始),对所有数据的k1关键字进行桶分配(因为,每个数字都是 0-9的,因此桶大小为10),再依次输出桶中的数据得到下面的序列。
930、063、083、184、505、278、008、109、589、269
再对上面的序列接着进行针对k2的桶分配,输出序列为:
505、008、109、930、063、269、278、083、184、589
最后针对k3的桶分配,输出序列为:
008、063、083、109、184、269、278、505、589、930
性能分析
很明显,基数排序的性能比桶排序要略差。每一次关键字的桶分配都需要O(N)的时间复杂度,而且分配之后得到新的关键字序列又需要O(N)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,当然d要远远小于N,因此基本上还是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。一般来说N>>M,因此额外空间需要大概N个左右。
但是,对比桶排序,基数排序每次需要的桶的数量并不多。而且基数排序几乎不需要任何“比较”操作,而桶排序在桶相对较少的情况下,桶内多个数据必须进行基于比较操作的排序。因此,在实际应用中,基数排序的应用范围更加广泛。