基数(cardinality)排序算法

基数排序是箱排序的改进和推广。

1、基数排序的基本思想

箱排序也称为桶排序(Bucket Sort),其基本思想是:设置若干箱子,依次扫描待排序的记录R[0],R[1],R[2]...R[n],把关键字等于k的记录全部装入到第k个箱子里(分配),然后按序号一次将各非空的箱子收尾连接起来(收集)。

例如,要将一副混洗的52张扑克牌按点数A<2<...<J<Q<K排序,需设置13个"箱子",排序时一次将每张牌按点数放入相应的箱子里,然后依次将这些箱子首尾相连,就得到按点数递增顺序排序的一副牌。

基数排序是基于多关键字的,什么是多关键字的呢?如果文件中任何一个记录R[i]的关键字都由d个分量构成,而且这d个分量中每个分量都是一个独立的关键字,则文件是多关键字的(比如扑克牌的两个关键字:点数和花色)。

通常实现多关键字的排序有两种方法:

最高位优先和最低位优先。

基数排序是典型的LSD排序方法,其基本思想是:从低位到高位依次对数据进行箱排序。在d趟箱排序中,所需的箱子书就是基数rd(可能的取值个数),这就是"基数排序"名称的由来。

2、思想

它是一种非比较排序。它是根据位的高低进行排序的,也就是先按个位排序,然后依据十位排序……以此类推。示例如下: 




3、算法复杂度

分配需要O(n),收集为O(r),其中r为分配后链表的个数,以r=10为例,则有0~9这样10个链表来将原来的序列分类。而d,也就是位数(如最大的数是1234,位数是4,则d=4),即"分配-收集"的趟数。因此时间复杂度为O(d*(n+r))。

4、稳定性 
          基数排序过程中不改变元素的相对位置,因此是稳定的!

5、代码实现

//使用基数排序对整数进行排序
//基数排序是复杂排序,适用于知道范围数据例如1-1000进行排序
//1.首先定义一个10个大小的指针数组,每个指针表示一个箱子,指针用于操作每个箱子
//2.在定义一个10个大小的数组,数组每个位置用于存储每个箱子装有多少元素
//3.找出数组中最大整数,得到最大整数的位数(每次除以10,记数++,直到number等于0为止)
//4.为指针数组中的指针开辟内存空间,并初始化为0
//5.以最小位数开始遍历,每次遍历之前清空记数数组(count)
//6.内层循环遍历数组a[0]-a[length-1],每次取得数组中元素第i位的数字xx,将元素放入数组xx中的第count[xx]位置,同时记数数组相应的数据++
//7.把所有数据存放到temp中之后,需按同位大小取出元素,根据count记数中每个记录的个数取出temp中所有元素.
//8.index=0,是从原始数组的a[0]开始,若记数为0的话,直接跳过取下一个箱子的元素(最内层循环体为a[index++]=temp[j][k],取出箱子每个元素放入a数组中)
//循环5-8步骤,直至最大值的最大位数循环完毕。
#include<iostream>
using namespace std;


//查找长度为length的数组的最大元素
int find_max(int a[], int length)		
{
	int max = a[0];							//max从a[0]开始
	for (int i = 1; i < length; i++)	
	{
		if (max<a[i])						//如果发现元素比max大,就重新给max赋值
		{
			max = a[i];
		}
	}
	return max;
}

//计算number有多少位
static int digit_number(int number)
{
	int digit = 0;
	do
	{
		number /= 10;					//除以10,记数+1
		digit++;
	} while (number!=0);					//直到等于0为止
	return digit;
}

//返回number上地Kth位的数字
static int kth_digit(int a, int i)
{
	int num = a/pow(10, i);					//除以10的几次方,取余
	return num%10;
}

//对长度为length的数组进行基数排序
static void radix_sort(int a[], int length)
{
	int* temp[10];						//指针数组,每一个指针表示一个箱子
	int i, j, k;							
	int count[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	 //用于存储每个箱子装有多少元素
	int max = find_max(a, length);				//取得数组中的最大整数
	int digitNum = digit_number(max);			//得到最大整数的位数
	for (i = 0; i < 10; i++)
	{
		temp[i] = new int[length];			//是每一个箱子能装下length个int元素
		memset(temp[i], 0, sizeof(int)*length);	<span style="white-space:pre">	</span>//初始化为0
	}
	for (i = 0; i < digitNum; i++)				//循环几位数次
	{
		memset(count, 0, sizeof(int)* 10);		//每次装箱前把count记数数组清空
		for (j = 0; j < length; j++)
		{
			int xx = kth_digit(a[j], i);		//取得每位数
			temp[xx][count[xx]] = a[j];		//将位数放入到对应的暂存数组中
			count[xx]++;				//此对应的箱子记数递增
		}
		int index = 0;						
		for (j = 0; j < 10; j++)			//取出所有箱子中的元素,将数据从暂存数组中取回
		{
			for (k = 0; k < count[j]; k++)	<span style="white-space:pre">	</span>//把箱子里所有的 元素都去回到原始数组中
			{
				a[index++] = temp[j][k];				
			}
		}
	}
}
static void print_array(int a[], int length)
{
	for (int i = 0; i < length; i++)			//打印数组
	{
		cout << a[i] << " ";
	}
}
void main9mianshiti8()
{
	int a[] = { 22, 32, 19, 53, 47, 29 };
	cout << "before print_array: ";
	print_array(a, 6);
	radix_sort(a, 6);					//基数排序
	cout << "\nafter print_array: ";
	print_array(a, 6);
	system("pause");
}
6、测试结果

before print_array:22 32 19 53 47 29

after print_array:19 22 29 32 47 53

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值