基数排序是一种高级排序算法。
基数排序需要进行d趟分配和收集,一趟分配需要O(n),一趟收集需要O( r ),所以基数排序的时间复杂度为O(d(n + r))(n表示待排序的记录数,d表示待排序数的最大位数,即所有记录中的最大关键字数,r则是每个关键字的取值范围)。
基数排序相当快,甚至比快速排序还快。但是它最大的缺点就是占内存太大,空间复杂度为O(r )。由于在代码中我们会用到11个辅助一维数组(实际上是一个二维数组和一个一维数组),若是对1亿个数进行排序,会占用100000000 * 11 * 4 / 1024 / 1024 / 1024 ≈ 4.1G
内存,若是你的内存比这个小,进行基数排序会报内存不足的错误:OutOfMemoryError: Java Heap Space
,所以根据你本机内存适当调整排序数量。
代码:
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[9999999];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*arr.length);
}
long start = System.currentTimeMillis();
radixSort(arr);
long end = System.currentTimeMillis();
System.out.println("基数排序耗时:" + (end - start) + "ms");
}
public static void radixSort(int[] arr){
int[][] buckets = new int[10][arr.length]; //10个桶,每个桶又是一维数组
int[] bucketNum = new int[10]; //每个桶当前存储数的数量
//找出最大位数(即代表循环轮数)
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max)
max = arr[i];
}
int circularNum = (max + "").length(); //循环轮数
for (int i = 0, n = 1; i < circularNum; i++, n *= 10) {
for (int value : arr) {
int bit = value / n % 10; //个位、十位、百位、... bit表示存到第几桶
buckets[bit][bucketNum[bit]++] = value;
}
//桶里的数按顺序存回arr
int index = 0;
for (int j = 0; j < buckets.length; j++) {
for (int k = 0; k < bucketNum[j]; k++) {
arr[index++] = buckets[j][k];
}
bucketNum[j] = 0; //清空该桶(实际上没有清空,不过后续都会被覆盖)
}
}
}
}
运行结果表示排序相当快,把空间换时间做到了极致,可能接近线性时间复杂度。比我们之前测试过的希尔排序、堆排序、归并排序、快速排序要快得多!