本系列文章共有十一篇:
冒泡排序及优化详解
快速排序及优化详解
插入排序及优化详解
希尔排序及优化详解
选择排序及优化详解
归并排序及优化详解
堆排序详解
计数排序及优化详解
桶排序详解
基数排序及优化详解
十大排序算法的比较与性能分析
一、基数排序基础
基数排序是一种非比较型整数排序算法,其基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
基数排序的具体做法是:
1>将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。
2>从最低位开始,依次进行一次排序。
3>依次从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
1.1 基数排序图示
以数组[11,54,46,25,23]为例,由于各元素位数相同,所以不用进行补位。先从个位开始进行比较排序,数组变成[11,23,54,25,46]。再按十位进行比较排序,数组变成[11,23,25,46,54],排序完成,图示如下:
1.2 基数排序实现
将上述过程用代码实现,示例为:
static void radixSort(int[] array, int digit) {
/*此处桶数之所以取10,是因为数字0-9总共是十个数字,因此每趟比较时,分为10个桶*/
int radix = 10;
int i = 0, j = 0;
/*存放各个桶的数据统计个数*/
int[] count = new int[radix];
int[] bucket = new int[array.length];
/*按照从低位到高位的顺序执行排序过程*/
for (int d = 1; d <= digit; d++) {
/*置空各个桶的数据统计*/
for (i = 0; i < radix; i++) {
count[i] = 0;
}
/*统计各个桶将要装入的数据个数*/
for (i = 0; i <array.length; i++) {
j = getDigit(array[i], d);
count[j]++;
}
/*count[i]表示第i个桶的右边界索引*/
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
/*将数据依次装入桶中,这里要从右向左扫描,保证排序稳定性*/
for (i = array.length-1; i >= 0; i--) {
j = getDigit(array[i], d);
/*求出元素的第k位的数字, 例如:576的第3位是5*/
bucket[count[j] - 1] = array[i];
/*放入对应的桶中,count[j]-1是第j个桶的右边界索引,对应桶的装入数据索引减一*/
count[j]--; //
}
/*将已分配好的桶中数据再倒出来,此时已是对应当前位数有序的表*/
for (i = 0, j = 0; i < array.length; i++, j++) {
array[i] = bucket[j];
}
}
}
/*获取x这个数的d位数上的数字,比如获取123的1位数,结果返回3*/
static int getDigit(int num, int d) {
/*本实例中的最大数是百位数,所以只要到100就可以了*/
int digit[] = {1, 10, 100};
return ((num/digit[d-1]) % 10);
}
二、基数排序优化
基数排序的优化多在于getDigit方法,即获取对应位数的数字上,如常规的该方法实现为:
int getDigit(int n,int i)
{
int p=(int)pow(radix,i);
return (int)(n*p)%radix;
}
pow方法的调用,可能会影响速度,可改为上面示例代码中的实现方法:
static int getDigit(int num, int d) {
int digit[] = {1, 10, 100};
return ((num/digit[d-1]) % 10);
}
三、稳定性、复杂度及适用场景
3.1 稳定性
基数排序是稳定的排序方法。
3.2 复杂度
在基数排序中,r为基数,d为位数。则基数排序的时间复杂度为O(d(n+r))。
3.3 适用场景
基数排序的适用场景和计数排序类似,即待排序序列是在一定范围内的整数。