基数排序
基数排序也称作桶排序,是一种当关键字为整数类型时非常高效的排序方法。
基本思想
设待排序的元素是m位d进制整(不足m位的关键字在高位补0),设置d个桶,令其编号分别为0,1,2…d-1。
首先元素最低位的数值一次把各个元素放到相应的桶中
然后按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各个桶中的数据元素。
这样就形成了数据元素集合的一个新的排列,称这样的一次排序过程为一次基数排序。
再对一次基数排序得到的数据元素按关键字次低位的数值依次把各个数据元素放到相应的桶中
按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素
这样的过程重复进行
当完成了m次基数排序后,就得到了排好序的数据元素序列
代码实现
因为要求进出桶中的数据元素序列满足先进先出的原则,因此这里所说的桶实际就是队列
队列有顺序队列和链式队列,因此在实现基数排序算法时,有基于顺序队列和链式队列两种不同的实现方式
C语言实现
#include<math.h>
testBS()
{
inta[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
int *a_p = a;
//计算数组长度
intsize = sizeof(a) / sizeof(int);
//基数排序
bucketSort3(a_p, size);
//打印排序后结果
inti;
for(i = 0; i < size; i++)
{
printf("%d\n", a[i]);
}
intt;
scanf("%d", t);
}
//基数排序
voidbucketSort3(int *p, intn)
{
//获取数组中的最大数
intmaxNum = findMaxNum(p, n);
//获取最大数的位数,次数也是再分配的次数。
intloopTimes = getLoopTimes(maxNum);
inti;
//对每一位进行桶分配
for(i = 1; i <= loopTimes; i++)
{
sort2(p, n, i);
}
}
//获取数字的位数
intgetLoopTimes(intnum)
{
intcount = 1;
inttemp = num / 10;
while(temp != 0)
{
count++;
temp = temp / 10;
}
returncount;
}
//查询数组中的最大数
intfindMaxNum(int *p, intn)
{
inti;
intmax = 0;
for(i = 0; i < n; i++)
{
if(*(p + i) > max)
{
max = *(p + i);
}
}
returnmax;
}
//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
voidsort2(int *p, intn, intloop)
{
//建立一组桶此处的20是预设的根据实际数情况修改
intbuckets[10][20] = {};
//求桶的index的除数
//如798个位桶index=(798/1)%10=8
//十位桶index=(798/10)%10=9
//百位桶index=(798/100)%10=7
//tempNum为上式中的1、10、100
inttempNum = (int)pow(10, loop - 1);
inti, j;
for(i = 0; i < n; i++)
{
introw_index = (*(p + i) / tempNum) % 10;
for(j = 0; j < 20; j++)
{
if(buckets[row_index][j] == NULL)
{
buckets[row_index][j] = *(p + i);
break;
}
}
}
//将桶中的数,倒回到原有数组中
intk = 0;
for(i = 0; i < 10; i++)
{
for(j = 0; j < 20; j++)
{
if(buckets[i][j] != NULL)
{
*(p + k) = buckets[i][j];
buckets[i][j] = NULL;
k++;
}
}
}
}
java实现
/**
* 基数排序
* 时间复杂度O(mn)
* 空间复杂度O(mn)
* 稳定的排序算法
*
* @param arr 排序数组
*/
public static void radixSort(int[] arr) {
int k = 0; //原数组下标计数器
int n = 1; //除数
int digitIndex = 1;//控制键值排序依据在哪一位
int maxDigit = getMaxDigit(arr);//该数组的最大位数
int[][] bucket = new int[10][arr.length];//数组第一维表示可能的余数为0-9
int[] order = new int[10];//order[i]表示改位是i的数的个数
while (digitIndex <= maxDigit) {
for (int i = 0; i < arr.length; i++) {
int lsd = ((arr[i] / n) % 10);
bucket[lsd][order[lsd]] = arr[i];//按照键值把数放置在桶内
order[lsd]++;
}
for (int i = 0; i < 10; i++) {
if (order[i] != 0) {
for (int j = 0; j < order[i]; j++) {//重新收集桶内的序列
arr[k] = bucket[i][j];
k++;
}
}
order[i] = 0;//清空
}
n *= 10;//接下来按照下一位进行放置
k = 0;
digitIndex++;
}
}
/**
* 获取整型数组中数据元素的最大位数
*
* @param arr 整型数组
* @return 最大位数
*/
private static int getMaxDigit(int[] arr) {
int max = getDigit(arr[0]);
for (int i = 1; i < arr.length; i++) {
if (getDigit(arr[i]) > max) {
max = getDigit(arr[i]);
}
}
return max;
}
/**
* 获取一个整数的位数
*
* @param i 整数
* @return 位数
*/
private static int getDigit(int i) {
return String.valueOf(i).length();
}
算法分析
基数排序算法要进行m次循环,每次循环中先要把n个数据元素放到相应的d个队列中,然后把各个队列中的数据元素收回,链式队列的插入算法和删除算法的时间复杂度均是O(1),因此基于链式队列的基数排序算法的时间复杂度为O(2mn)或者简写成O(mn)
基于链式队列的基数排序算法要m次使用n个节点存放n个数据元素,因此空间复杂度是O(n)
从算法的思想来看,基数排序算法是一种稳定的排序算法