排序算法-10-基数排序

基数排序

前面介绍的计数排序是一种线性时间(即时间复杂度为 O ( n ) O(n) O(n))的排序算法,这一篇介绍另外一种也是线性时间的排序方法:基数排序。

原理

基数排序最早是用于打孔卡片制表机上的算法,它可以给打孔的卡片进行排序。我们把打孔改为用数字表示,则它的基本思想是将整数按位数依次排序。步骤如下:

  1. 统一数据长度为相同的位数,数位较短的数前面补零。
  2. 按照个位数进行排序。
  3. 按照十位数进行排序。
  4. 按照百位数进行排序。
  5. 依次类推,一直到按照最高位进行排序。然后就结束了。

其中对每个位数进行排序时需要用稳定的排序算法。如果这个稳定的排序算法可以在线性时间完成,则基数排序也可以在线性时间完成。典型的,我们可以用前面介绍过的计数排序

实现

按照以上原理我们来用代码实现。

下面就是用C语言实现的代码。

  • 要排序的数组a有n个元素。分两个函数来实现。
  • radix_sort 中先找到a中元素的最大值,再按给定基数radix计算最大位数,然后从低位到最高位依次调用 count_sort_radix 进行排序。注意基数radix的值是可以调整的,其大小会影响排序效率。
  • count_sort_radix 是由前面介绍过的计数排序修改而来,按第d位进行计数排序,注意a中所有元素的每一位数的值都在[0,k]范围内。
/* a中所有元素的每一位数的值都在[0,k]范围内。 */
/* d表示按第几位进行排序,0表示按个位排序,   */
/*   1表示按十位,2表示按百位,以此类推。     */
void count_sort_radix(int a[], int n, int k, int d)
{
	int *b, *c;
	int i;
	int num = 1; //便于提取出a中的某一位数的值

	b = (int*)malloc(sizeof(int)*n);
	if (b==NULL) return;
	c = (int*)malloc(sizeof(int)*(k+1));
	if (c==NULL) {
		free(b);
		return;
	}

	memset(c, 0, sizeof(int)*(k+1));

	while (d-->0) num *= k+1;

	for (i=0; i<n; i++) {
		c[a[i]/num%(k+1)]++;
	} //此时c[i]中存的是a中有几个数的d位为i

	for (i=1; i<=k; i++) {
		c[i] += c[i-1];
	} //此时c[i]中存的是a中有几个数的d位<=i

	for (i=n-1; i>=0; i--) {
		b[c[a[i]/num%(k+1)]-1] = a[i];   //将a[i]放入b中正确的位置
		c[a[i]/num%(k+1)]--;             //调整b的位置指针,避免相同元素放到相同位置
	}

	memcpy(a, b, sizeof(int)*n); //将排序结果复制到a中

	free(c);
	free(b);
}


void radix_sort(int a[], int n)
{
	int i, max_value, max_digit;
	int radix = 10;

	/* 找出数组a中的最大值 */
    max_value = a[0];
    for (i=1; i<n; i++)
        if (a[i] > max_value) max_value = a[i];

	/* 计算最大位数 */
	max_digit = 0;
	while (max_value) {
		max_digit++;
		max_value /= radix;
	}

	for (i=0; i<max_digit; i++)
		count_sort_radix(a, n, radix-1, i);
}

为了验证此函数的效果,加上了如下辅助代码,对3个数组进行排序,运行结果在最后,可见排序成功。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE_ARRAY_1 5
#define SIZE_ARRAY_2 6
#define SIZE_ARRAY_3 20

void show_array(int a[], int n);
void radix_sort(int a[], int n);

void main()
{
	int array1[SIZE_ARRAY_1]={11,224,32,9,0};
	int array2[SIZE_ARRAY_2]={1000,53,221,1,229,222};
	int array3[SIZE_ARRAY_3];

	for(int i=0; i<SIZE_ARRAY_3; i++) {
		array3[i] = (int)((10000.0*rand())/(RAND_MAX+1.0));
	}

	printf("Before sort, ");
	show_array(array1, SIZE_ARRAY_1);
	radix_sort(array1, SIZE_ARRAY_1);
	printf("After sort, ");
	show_array(array1, SIZE_ARRAY_1);

	printf("Before sort, ");
	show_array(array2, SIZE_ARRAY_2);
	radix_sort(array2, SIZE_ARRAY_2);
	printf("After sort, ");
	show_array(array2, SIZE_ARRAY_2);

	printf("Before sort, ");
	show_array(array3, SIZE_ARRAY_3);
	radix_sort(array3, SIZE_ARRAY_3);
	printf("After sort, ");
	show_array(array3, SIZE_ARRAY_3);
}

void show_array(int a[], int n)
{
	if(n>0)
		printf("This array has %d items: ", n);
	else
		printf("Error: array size should bigger than zero.\n");

	for(int i=0; i<n; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

运行结果:

Before sort, This array has 5 items: 11 224 32 9 0
After sort, This array has 5 items: 0 9 11 32 224
Before sort, This array has 6 items: 1000 53 221 1 229 222
After sort, This array has 6 items: 1 53 221 222 229 1000
Before sort, This array has 20 items: 8401 3943 7830 7984 9116 1975 3352 7682 2777 5539 4773 6288 3647 5134 9522 9161 6357 7172 1416 6069
After sort, This array has 20 items: 1416 1975 2777 3352 3647 3943 4773 5134 5539 6069 6288 6357 7172 7682 7830 7984 8401 9116 9161 9522

分析

时间复杂度

从代码可见,若要排序的数位数为d,则 radix_sort 中调用d次 count_sort_radix,而计数排序的时间复杂度为 O ( n ) O(n) O(n),所以基数排序的时间复杂度为 O ( d n ) O(dn) O(dn),因d为常数,所以基数排序的时间复杂度为 O ( n ) O(n) O(n)

虽然其时间复杂度为 O ( n ) O(n) O(n),但隐藏在其后的常数因子一般较大,所以实际应用中选择什么样的排序算法还要视情况而定。

空间复杂度

因为基数排序调用计数排序,而计数排序的空间复杂度为 O ( n ) O(n) O(n),所以基数排序的空间复杂度为 O ( n ) O(n) O(n)

稳定性

稳定。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值