基数排序(radix sorting)将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。 然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
假设有一些二元组(a,b ),要对它们进行以a为首要关键字,b的次要关键字的排序。我们可以先把它们先按照首要关键字排序,分成首要关键字相同的若干堆。然后,在按照次要关键值分别对每一堆进行单独排序。最后再把这些堆串连到一起,使首要关键字较小的一堆排在上面。按这种方式的基数排序称为MSD(Most Significant Dight)排序。第二种方式是从最低有效关键字开始排序,称为LSD(LeastSignificantDight)排序。首先对所有的数据按照次要关键字排序,然后对所有的数据按照首要关键字排序。要注意的是,使用的排序算法必须是稳定的,否则就会取消前一次排序的结果。由于不需要分堆对每堆单独排序,LSD方法往往比MSD简单而开销小。下文介绍的方法全部是基于LSD的。
基数排序的简单描述就是将数字拆分为个位十位百位,每个位依次排序。因为这对算法稳定要求高,所以我们对数位排序用到上一个排序方法计数排序。因为基数排序要经过d(数据长度)次排序, 每次使用计数排序, 计数排序的复杂度为 On), d 相当于常量和N无关,所以基数排序也是 O(n)。基数排序虽然是线性复杂度, 即对n个数字处理了n次,但是每一次代价都比较高, 而且使用计数排序的基数排序不能进行原地排序,需要更多的内存,并且快速排序可能更好地利用硬件的缓存,所以比较起来,像快速排序这些原地排序算法更可取。对于一个位数有限的十进制数,我们可以把它看作一个多元组,从高位到低位关键字重要程度依次递减。可以使用基数排序对一些位数有限的十进制数排序。
具体实例及实现代码如下:
/**
* @Title: RadixSorting.java
* @Package sortandsearch
* @Description: TODO
* @author peidong
* @date 2017-5-8 上午10:14:55
* @version V1.0
*/
packagesortandsearch;
/**
* @ClassName: RadixSorting
* @Description: 基数排序实例
* @date 2017-5-8 上午10:14:55
*
*/
publicclass RadixSorting {
/**
*
*@Title: radixSorting
*@Description: 基数排序实现
*@param arr
*@param d
* 数组长度
*@return void
*@throws
*/
public static void radixSorting(int[]arr, int d) {
for (int i = 0; i < d; i++) {
// 依次对各位数字进行排序
arr = countingSort(arr, i);
print(arr, i + 1, 0);
}
}
/**
*
*@Title: print
*@Description: 打印
*@param arr
*@param k
*@param d
*@return void
*@throws
*/
public static void print(int[] arr, intk, int d) {
if (k == d)
System.out.println("最终排序结果为:");
else
System.out.println("按第" + k + "位排序后,结果为");
for (int t : arr) {
System.out.print(t + ""); // 输出
}
System.out.println();
}
/**
*
*@Title: countingSort
*@Description: 按位排序实现
*@return
*@return int[]
*@throws
*/
public static int[] countingSort(int[]arr, int index) {
// 定义变量
int k = 9;
int[] res = new int[arr.length];
int[] temp = new int[k + 1];
// 赋值与排序
for (int i = 0; i < k; i++) {
temp[i] = 0;
}
for (int i = 0; i < arr.length;i++) {
int t = getBitData(arr[i],index);
temp[t]++;
}
for (int i = 1; i <= k; i++) {
temp[i] += temp[i - 1];
}
for (int i = arr.length - 1; i>= 0; i--) {
int m = getBitData(arr[i],index);
res[temp[m] - 1] = arr[i];
temp[m]--;
}
return res;
}
/**
*
*@Title: getBitData
*@Description: 获取指定位数数据
*@param data
*@param index
*@return
*@return int
*@throws
*/
public static int getBitData(int data,int index) {
while (data != 0 && index> 0) {
data /= 10;
index--;
}
return data % 10;
}
/**
*@Title: main
*@Description: 测试基数排序
*@param args
*@return void
*@throws
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = new int[] { 326, 453,608, 835, 751, 435, 704, 690, 88, 79, 63, 97};
System.out.println("基数排序前为:");
for (int t : arr) {
System.out.print(t + "");
}
System.out.println();
radixSorting(arr, 3);
}
}
基数排序的时间复杂度是 O(k·n),其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(n·log(n)),因为k的大小一般会受到 n 的影响。 以排序n个不同整数来举例,假定这些整数以B为底,这样每位数都有B个不同的数字,k就一定不小于logB(n)。由于有B个不同的数字,所以就需要B个不同的桶,在每一轮比较的时候都需要平均n·log2(B) 次比较来把整数放到合适的桶中去,所以就有:
k 大于或等于 logB(n),每一轮(平均)需要 n·log2(B) 次比较
所以,基数排序的平均时间T就是:
T ≥ logB(n)·n·log2(B) = log2(n)·logB(2)·n·log2(B) = log2(n)·n·logB(2)·log2(B) = n·log2(n)
所以和比较排序相似,基数排序需要的比较次数:T ≥ n·log2(n)。故其时间复杂度为 Ω(n·log2(n)) = Ω(n·log n) 。*/