基数排序是在桶排序的基础上发展而来的,两种排序都是分配排序的高级实现。将数组中的所有数按位进行分类,由于每一位数的大小都在0~9之间,因此创建下标为0~9的十个数组,根据需要对数进行存储。
图解:
例如对{12 45 3 15 61 154 242 58 46 9}进行排序的步骤如下 :
第一轮,建立10个桶,也就是数组,将数组中的数依次取出,按照个位数字的不同依次放入不同的桶中:
放入桶中之后:
然后,按照桶的顺序(下标顺序)依次将所有桶中的元素取出放回到原来的数组:61 12 242 3 154 45 15 46 58 9
第二轮,再次将数组中的数依次取出,按照十位数字的不同依次放入不同的桶中,如果十位上没有数字则补零:
然后,按照桶的顺序(下标顺序)依次将所有桶中的元素取出放回到原来的数组:3 9 12 15 242 45 46 154 58 61
第三轮, 再次将数组中的数依次取出,按照百位数字的不同依次放入不同的桶中,如果百位上没有数字则补零:
然后,按照桶的顺序(下标顺序)依次将所有桶中的元素取出放回到原来的数组:3 9 12 15 45 58 61 154 242,此时已经排序完成。
代码
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int arr[] = {12, 45, 3, 15, 61, 154, 242, 58, 46, 9};
sort(arr);
}
public static void sort(int[] arr) {
//定义一个二维数组,表示10个桶
//二维数组包含10个一维数组,为了防止移除,每个一维数组的大小都要容得下arr中所有的元素
//因此基数排序算法就是以空间换时间的算法,比较耗费空间
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中存放了多少数据,定义一个一维数组存放每个桶中数据的个数
//num[n]表示第n个位置已经存放的元素个数
int[] num = new int[10];
//得到数组中最大数的位数
int max = arr[0];
for(int i=0; i<arr.length; i++) {
if(arr[i]>max) {
max = arr[i];
}
}
//最大数的位数
int maxLength = (max+"").length();
int n = 1;
for(int i=0; i< maxLength; i++) {
for(int j=0; j<arr.length; j++)
{ //取出每个元素的对应的位
int ge = arr[j] / n %10;
n = n*10; //下次取十位,再下次取百位
//放入相应的桶当中
bucket[ge][num[ge]] = arr[j];
//该数组中元素+1
num [ge]++;
}
//依次取出每个桶中的元素放回到原数组
int index = 0;
for(int k=0; k<10; k++) {//遍历每一个桶
//如果桶中有数据就取出
if(num[k] != 0) {
for(int m=0; m<num[k];m++) {
arr[index++] = bucket[k][m];
}
}
//num清零
num[k] = 0;
}
System.out.println("第"+ (i+1) +"轮结果"+ Arrays.toString(arr));
}
}
}
特点
- 时间复杂度:平均、最好、最坏都为O(k*n),其中k为常数,n为元素个数
- 空间复杂度:O(n+k)
- 基数排序是对传统桶排序的扩展,速度很快.
- 基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。
- 基数排序时稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]