1.基数排序简单介绍
基数排序是通过分析元素的各个位置的具体值,将要排序的元素分配到某些桶中,达到排序的效果
它是稳定性的排序,是使用空间来换取时间的经典例子
它是桶排序的扩展
2.基数排序的思想
在每次排序的时候,创建序号为0-9的10个桶,将所有待比较的数值统一为同样的数位长度,数位较短的数前面补0,然后从最低位开始,依次进行排序。这样从最低位直到最高位排序完成之后,数列就变成了一个有序序列
3.举个栗子
53, 3, 542, 748, 14, 214 使用基数排序, 进行升序排序 第一轮: 542 53 3 14 214 748 -- 按照个位排序 第二轮: 3 14 214 542 748 53 -- 按照十位排序 第三轮 3 14 53 214 548 713 -- 按照百位排序 此时已经排序完毕.
来张图康康
4.代码一览
准备原数组arr
准备一个二维数组标识哪个容器中的哪个数据 bucket[10][arr.length]
准备一个数组来标识桶中实际的数据值
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
//bucketElementCount[0] 就是bucket[0]中记录数据的数量
int[] bucketElementCount = new int[10]; //数组的默认值为0
//开始处理第一轮操作,取出每个元素的个位
开始遍历数组元素,并放在不同的桶中
for (int j = 0; j < arr.length; j++) {
//得到个位数
int digitOfElement = arr[j] / 1 % 10;
//将当前的数据放入到对应桶中
/**
* bucket[digitOfElement]: 表示放在哪个容器中
* bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
* */
bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
bucketElementCount[digitOfElement]++;
}
最后将桶中的数据取出,归还给原来的数组arr
//按照桶的数据(取出数据放入数组中)
int index = 0;
//开始遍历每一个桶,并将桶中的数据放入到元素中
//bucketElementCount:10
for (int k = 0; k < bucketElementCount.length; k++) {
//如果桶中有数据才放入到原数组中
if (bucketElementCount[k] != 0) {
//
for (int l = 0; l < bucketElementCount[k]; l++) {
//取出数据放入到原来数组
arr[index++] = bucket[k][l];
}
}
//第一轮处理后,需要将每个bucketElementCounts清零
}
System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
for (int i : bucketElementCount) {
bucketElementCount[i] = 0;
}
System.out.println("第一轮对个位的排序处理 arr=" + Arrays.toString(arr));
}
以上就是第一轮的操作了,接下来两轮如法炮制
//第二轮排序(针对每个元素的十位进行排序处理)
public static void radixSecondSort(int[] arr) {
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
//bucketElementCount[0] 就是bucket[0]中记录数据的数量
int[] bucketElementCount = new int[10]; //数组的默认值为0
//开始处理第二轮操作,取出每个元素的十位
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / 10 % 10;
//将当前的数据放入到对应桶中
/**
* bucket[digitOfElement]: 表示放在哪个容器中
* bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
* */
//开始放置数据
bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
bucketElementCount[digitOfElement]++;
}
//按照桶的数据(取出数据放入数组中)
int index = 0;
//开始遍历每一个桶,并将桶中的数据放入到元素中
//bucketElementCount:10
for (int k = 0; k < bucketElementCount.length; k++) {
//如果桶中有数据才放入到原数组中
if (bucketElementCount[k] != 0) {
//
for (int l = 0; l < bucketElementCount[k]; l++) {
//取出数据放入到原来数组
arr[index] = bucket[k][l];
index++;
}
}
}
System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
//清空bucketElementCount 为下一次做准备
for (int i : bucketElementCount) {
bucketElementCount[i] = 0;
}
System.out.println("第二轮对十位的排序处理 arr=" + Arrays.toString(arr));
}
//第三轮排序(针对每个元素的百位进行排序处理)
public static void radixThirdSort(int[] arr) {
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
//bucketElementCount[0] 就是bucket[0]中记录数据的数量
int[] bucketElementCount = new int[10]; //数组的默认值为0
//开始处理第二轮操作,取出每个元素的十位
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / 100 % 10;
//将当前的数据放入到对应桶中
/**
* bucket[digitOfElement]: 表示放在哪个容器中
* bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
* */
//开始放置数据
bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
bucketElementCount[digitOfElement]++;
}
//按照桶的数据(取出数据放入数组中)
int index = 0;
//开始遍历每一个桶,并将桶中的数据放入到元素中
//bucketElementCount:10
for (int k = 0; k < bucketElementCount.length; k++) {
//如果桶中有数据才放入到原数组中
if (bucketElementCount[k] != 0) {
//
for (int l = 0; l < bucketElementCount[k]; l++) {
//取出数据放入到原来数组
arr[index] = bucket[k][l];
index++;
}
}
}
System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
//清空bucketElementCount 为下一次做准备
for (int i : bucketElementCount) {
bucketElementCount[i] = 0;
}
System.out.println("第三轮对百位的排序处理 arr=" + Arrays.toString(arr));
}
在写完三轮循环后,发现了三轮循环有着很多很多共同点 唯一的区别就是判断是以个位 or 十位 or 百位进行区分,换言之
就是如何根据最大数的位数进行数据的区分
个位 num/1%10
十位 num/10%10
百位 num/100%10
...
所以我们需要解决如何寻找最大数的位数,然后再去由此外套一层for循环,得到最终的基数排序算法
/**
* 得到数组中最大数的位数
* int max=arr[0];//假设第一个数就是最大数
* for(int i=1;i<arr.length;i++){
* if(arr[i]>max){
* max=arr[i];
* }
* }
* //得到最大数是几位数(将max强转为string 然后求出长度)
* int maxLength=(max+"").length();
*/
最终轮代码:
public static void radixFinalSort(int[] arr) {
//寻找数组的一些属性
int max = arr[0];//假设第一个数就是最大数
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//得到最大数是几位数
int maxLength = (max + "").length();
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
//bucketElementCount[0] 就是bucket[0]中记录数据的数量
int[] bucketElementCount = new int[10]; //数组的默认值为0
//最外层循环
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / n % 10;
//将当前的数据放入到对应桶中
/**
* bucket[digitOfElement]: 表示放在哪个容器中
* bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
* */
//开始放置数据
bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
bucketElementCount[digitOfElement]++;
}
//按照桶的数据(取出数据放入数组中)
int index = 0;
//开始遍历每一个桶,并将桶中的数据放入到元素中
//bucketElementCount:10
for (int k = 0; k < bucketElementCount.length; k++) {
//如果桶中有数据才放入到原数组中
if (bucketElementCount[k] != 0) {
//
for (int l = 0; l < bucketElementCount[k]; l++) {
//取出数据放入到原来数组
arr[index++] = bucket[k][l];
}
}
bucketElementCount[k]=0;
}
System.out.println("第"+(i+1)+"轮"+"排序处理为"+Arrays.toString(arr));
}
}
主函数:
public static void main(String[] args) {
int arr[] = {53, 3, 542, 748, 14, 214};
// radixFirstSort(arr);
// radixSecondSort(arr);
// radixThirdSort(arr);
radixFinalSort(arr);
}
注:由于基数排序是一个使用空间换取时间的栗子,所以在数据量特别大的时候不建议使用
假设 有八千万个int值进行排序 运存为(80000000*11*4/1024/1024/1024)3.3GB,会导致OutOfMemoryError: Java heap space
所以数据量过大的时候不建议使用基数排序