基数(桶)排序算法详解之C语言版

一、算法原理

基数排序算法又称桶排序,是一种原理简单实现相对麻烦一点的算法。基数排序属于稳定排序法,适用于数值比较大的数据之间的排序。
常见的内部排序算法中都使用到了元素之间的比较大小,而基数排序算法不涉及元素之间的比较,而是根据基数将数据存放到相应的“桶”里,经过多趟这样的存放过程,也就是一个渐进排序过程,就可以实现对一组散乱数据进行排序。总趟数是数组中位数最多的那个元素的位数。其实读到这里,大家是不是有一种很熟悉的感觉,没错,这跟创建Hash(散列)表的原理类似。
第一趟排序的基数是1,即从“个位”上的数字开始,将根据每一个元素的“个位”数字将该元素存到到与数字相同的“桶里”,得到一次排序结果。第二趟排序的基数是10,在第一趟排序的基础上,根据“百位”上的数字将元素重新放入相应的“桶”里,得到新一轮的排序。依次类推,直到所有元素中的最高位已经处理完毕,结束整个排序过程。从这里就可以看出基数排序就是一个渐进的排序过程。
需要注意的是使用静态数组进行基数排序时,并不是真的把元素放入“桶”里,而是把某个“位”上数字相同的元素个数存放入“桶”里。这样做是为了方便排序。“桶”的个数是10,这是因为阿拉伯数字就是10个,分别是0,1,2,3,4,5,6,7,8,9。每个桶中的值就是相同“位”上数字相同的元素个数。

下面以具体的例子演示一下基数排序的基本原理。
Demo:设有数据如下:
在这里插入图片描述
准备工作
1)创建10个桶。
2)求出最大的元素12608(也就是这里涉及到了比较大小,此外再无,童叟无欺),计算出其总位数是5,用于控制排序的总趟数。其实每一趟就是针对一个“位”上的元素进行排序。
3)为了方便求每一个元素不同“位”上的元素,设定一个基数,其取值分别是1,10,100,…,即第一趟排序时,值为1,用于求“个位”上的数字;第二趟排序时值为10,用于求“十位”上的数字,第三趟排序时,值为100,用于求“百位”上的数字,等等。
第一趟排序:
基数是1,根据“个位”数字将元素放入对应的桶里。
在这里插入图片描述
之后将根据“桶”号大小,将“桶”里元素按照桶号的大小顺序放入数组,得到如下结果:
在这里插入图片描述
第二趟排序:
基数是10,在第一趟排序的基础上,根据“十位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第三趟排序:
基数是100,在第二趟排序的基础上,根据“百位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第四趟排序:
基数是1000,在第三趟排序的基础上,根据“千位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第五趟排序:
基数是10000,在第四趟排序的基础上,根据“万位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
至此,基数排序结束。
在上述演示过程中,可以发现,在每一趟的排序中,每个“桶里”存放的元素个数都可能不同,如果直接把元素存放到桶里,就给算法的具体实现带来了很大麻烦。为了解决这个麻烦,“桶”里不再存储元素,而是改为存储元素的个数,同时引入一个新的数组作为缓存,存储每一趟的排序结果,之后再把排序结果存储到原数组中,这样就可以方便的实现桶排序了。
例如第一趟排序中的“桶”里数据就行可以修改为存放元素的个数,具体如下:
在这里插入图片描述
再做元素值的累加得到:
在这里插入图片描述
这样就可以得到原数组中每一个元素在排序后数组中的下标。有童鞋会突然发现有的桶里存放了多个元素,如何解决其位置呢?这个其实很简单,“桶”的元素值每用一次,就执行减1操作,就可以解决了。例如用bucket表示桶数组,bucket[4]中实际存放了两个元素,对应的原数组的元素分别是14和8844。bucket[4]的值是5,表示其中存放的元素在新数组的下标值是5,即当取走元素8844时,8844在新数组的下标是5,然后bucket[4]执行减1操作,其值就是4了,再取走元素14时,元素14在新数组的下标就是4了。

二、基数排序算法

记待排序的数组为data,元素个数为length。
**Step1:**创建桶bucket,有10个元素,初始值均为0,用于存放某“位”数字与桶元素编号相同的数组元素的个数;
求出数组的最大元素,同时算出其总位数count;
定义基数变量,例如base,其初始值为1;
分配缓存buff,其元素个数与原数组元素个数相同。
**Step 2:**数组的每个元素data[i]除以base之后对10求余数,记为k,执行bucket[k]++;转下一步;
**Step 3:**重新计算“桶”bucket中的每一个元素的值,让其等于前面元素之和,即
bucket[k+1] += bucket[k]
这样就可以计算出原数组 data中的每一个元素在新数组buff中的位置。(这一步是不是很神奇);转下一步;
**Step 4:**根据“桶”里的元素,也就是位置序号,将原数组data中的元素依次存放到buff中,再将buff中元素依次存入data中;转下一步;
**Step 5:**base *= 10,count–,判断count > 0, 如果为真,则转step2,否则结束排序。
三、基数排序的C程序
1. 基数排序代码

//利用桶排序算法对数组data进行从小到大排序 
void RadixSort( int data[], int length )
{
	int i, k, maxVal, count, base;
	int bucket[10] = { 0 };
	int *buff = new int[ length ];
	//求数组中的最大元素 
	maxVal = data[0];
	for( i = 1; i < length; i++ )
	{
		if( data[i] > maxVal )
		{
			maxVal = data[i];
		}
	}
	//printf( "maxVal = %d\n", maxVal );
	//计算最大元素的位数 
	count = 0;
	while( maxVal > 0 )
	{
		count++;
		maxVal /= 10;
	}
	//printf( "count = %d\n", count );
	base = 1;
	for( k = 1; k <= count; k++ )
	{
		//求每一个元素的 base位上的数字,对应的桶元素做累加 
		for( i = 0; i < length; i++  ) 
		{
			bucket[ ( data[i] / base ) % 10 ]++;
		}
		//重新计算桶元素的值,每一个元素都是其前面元素之和
		//如此操作,可以得到每一个桶中的元素在排序之后的位置下标 
		for( i = 1; i < 10; i++ ) 
		{
			bucket[i] += bucket[i-1];
		}
		//根据桶元素的值,将data中的元素重新排位存入buff中
		for( i = length-1; i >= 0; i-- ) 
		{
			buff[ bucket[ ( data[i] / base ) % 10 ] - 1 ] = data[i];
			bucket[ ( data[i] / base ) % 10 ]--;
		}
		//将buff中元素对位存放到data中
		for( i = 0; i < length; i++ ) 
		{
			data[i] = buff[i];
		}
		//清空桶,即让桶里元素归0 
		for( i = 0; i < 10; i++ ) 
		{
			bucket[i] = 0;
		}
		//base的值乘以10,之后进入下一趟排序 
		base *= 10;
	}
	delete[] buff;
}

2.完成测试代码

#include"stdio.h" 
void RadixSort( int data[], int length );

int main()
{
	int data[] = { 255, 12608, 31, 14, 21, 121, 268, 8844 };
	int length = 8;
	printf( " Initial Array    : " );
	for( int i = 0; i < length; i++ )
	{
		printf( "%8d", data[i] );
	}
	printf( "\n" );
	RadixSort( data, length );
	return 0;
}
//利用桶排序算法对数组data进行从小到大排序 
void RadixSort( int data[], int length )
{
	int i, k, maxVal, count, base;
	int bucket[10] = { 0 };
	int *buff = new int[ length ];
	//求数组中的最大元素 
	maxVal = data[0];
	for( i = 1; i < length; i++ )
	{
		if( data[i] > maxVal )
		{
			maxVal = data[i];
		}
	}
	//printf( "maxVal = %d\n", maxVal );
	//计算最大元素的位数 
	count = 0;
	while( maxVal > 0 )
	{
		count++;
		maxVal /= 10;
	}
	//printf( "count = %d\n", count );
	base = 1;
	for( k = 1; k <= count; k++ )
	{
		//求每一个元素的 base位上的数字,对应的桶元素做累加 
		for( i = 0; i < length; i++  ) 
		{
			bucket[ ( data[i] / base ) % 10 ]++;
		}
		//重新计算桶元素的值,每一个元素都是其前面元素之和
		//如此操作,可以得到每一个桶中的元素在排序之后的位置下标 
		for( i = 1; i < 10; i++ ) 
		{
			bucket[i] += bucket[i-1];
		}
		//根据桶元素的值,将data中的元素重新排位存入buff中
		for( i = length-1; i >= 0; i-- ) 
		{
			buff[ bucket[ ( data[i] / base ) % 10 ] - 1 ] = data[i];
			bucket[ ( data[i] / base ) % 10 ]--;
		}
		//将buff中元素对位存放到data中
		for( i = 0; i < length; i++ ) 
		{
			data[i] = buff[i];
		}
		//清空桶,即让桶里元素归0 
		for( i = 0; i < 10; i++ ) 
		{
			bucket[i] = 0;
		}
		//base的值乘以10,之后进入下一趟排序 
		base *= 10;
		
		//
		//向屏幕输出每一趟排序结果,便于观察 
		printf( " No. %d time sort : ", k );
		for( i = 0; i < length; i++ ) 
		{
			printf( "%8d", data[i] );
		}
		printf( "\n" );
		//
	}
	delete[] buff;
}

3.测试运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值