基数排序(桶排序)
1.定义:
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,借以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法(截取自百度百科)。
2.原理
/**
* 基数排序的思想
* 首先定义10个桶 ,然后对数组中的数据进行按个位、十位、百位、千位 来分配到相应的桶中
* 1.比如: arr这样一个数组数据 那么第一轮按照每位数据的个位数进行分桶
* 桶的位数 0 1 2 3 4 5 6 7 8 9
* 数据: 32 3 54 256
* 724 16
* 经过第一轮排序后位: 32,3,54,724,256,16
* 2. 第二轮按照十位来分
* 桶的位数 0 1 2 3 4 5 6 7 8 9
* 数据: 3 16 724 32 54
* 256
* 进过第二轮排序后: 3,16,724,32,54,256
* 3.第三轮按照百位来分
* 桶的位数 0 1 2 3 4 5 6 7 8 9
* 数据: 3 256 724
* 16
* 32
* 54
* 进过第三轮排序后: 3,16,32,54,256,724
* 说明:
* 1.排序的次数按照数组中最大位数来的
* 2.当位数不足时,在位数前补0
*
* 需注意:
* 1.定义10个桶 刚好对应0-9这个10个数字 然后每个桶需要记录桶里面数据的个数
3.代码实现
下面是每一步推导的过程,结合上面的算法思想去理解就会很容易了。
public static void radixSort(int[] arr) {
//首先需要定义桶的数量
int [][] msg=new int[10][arr.length];
//用来记录每个桶装了几个数据
int [] backElment=new int[10];
for(int j=0;j<arr.length;j++) {
//取出个位数据
int temp=arr[j]/1 % 10;
//放到对应的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照这个桶的顺序(一维数组的下标一次取出数据,放入到原来的数组中)
int index=0;
//遍历每一个桶,并将桶中的数据,放到原数组中
for(int i=0;i<backElment.length;i++) {
//如果桶中有数据,则将桶中数据循环放入原数组中
if(backElment[i]!=0) {
//循环该桶 即第I桶(即第i个一维数组) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
//取出数组后将该桶置为0
backElment[i]=0;
}
System.out.println("第一轮排序后的结果:===>"+Arrays.toString(arr));
for(int j=0;j<arr.length;j++) {
//取出个位数据
int temp=arr[j]/10 % 10;
//放到对应的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照这个桶的顺序(一维数组的下标一次取出数据,放入到原来的数组中)
index=0;
//遍历每一个桶,并将桶中的数据,放到原数组中
for(int i=0;i<backElment.length;i++) {
//如果桶中有数据,则将桶中数据循环放入原数组中
if(backElment[i]!=0) {
//循环该桶 即第I桶(即第i个一维数组) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
backElment[i]=0;
}
System.out.println("第二轮排序后的结果:===>"+Arrays.toString(arr));
for(int j=0;j<arr.length;j++) {
//取出个位数据
int temp=arr[j]/100 % 10;
//放到对应的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照这个桶的顺序(一维数组的下标一次取出数据,放入到原来的数组中)
index=0;
//遍历每一个桶,并将桶中的数据,放到原数组中
for(int i=0;i<backElment.length;i++) {
//如果桶中有数据,则将桶中数据循环放入原数组中
if(backElment[i]!=0) {
//循环该桶 即第I桶(即第i个一维数组) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
}
System.out.println("第三轮排序后的结果:===>"+Arrays.toString(arr));
}
最终代码
/**
* 优化后的桶排序算法
* @param arr
*/
public static void radixSort2(int[] arr) {
// 桶排序的循环次数是根据数组中最大位数的来定义
// 1 查找到数组中最大的数据 获取位数 我们可以吧第一位定义为最大的数字信息
int maxValue = arr[0];
for (int j = 1; j < arr.length; j++) {
if (maxValue < arr[j]) {
maxValue = arr[j];
}
}
// 得到循环的次数
int arrCount = (maxValue + "").length();
// 首先需要定义桶的数量
int[][] msg = new int[10][arr.length];
// 用来记录每个桶装了几个数据
int[] backElment = new int[10];
for (int i = 0, j = 1; i < arrCount; i++, j *= 10) {
for (int k = 0; k < arr.length; k++) {
// 取出个位数据
int temp = arr[k] / j % 10;
// 放到对应的桶中
msg[temp][backElment[temp]] = arr[k];
backElment[temp]++;
}
// 按照这个桶的顺序(一维数组的下标一次取出数据,放入到原来的数组中)
int index = 0;
// 遍历每一个桶,并将桶中的数据,放到原数组中
for (int l = 0; l < backElment.length; l++) {
// 如果桶中有数据,则将桶中数据循环放入原数组中
if (backElment[l] != 0) {
// 循环该桶 即第I桶(即第i个一维数组) 放入
for (int k = 0; k < backElment[l]; k++) {
arr[index++] = msg[l][k];
}
}
// 取出数组后将该桶置为0
backElment[l] = 0;
}
//System.out.println("第"+(i+1)+"轮排序后的结果:===>"+Arrays.toString(arr));
}
}
4 测试性能
public static void main(String[] args) {
int [] arr= {54,16,32,3,256,724,1024};
radixSort(arr);
System.out.println("--------------");
//我们用数据来测试一下基数排序(桶排序)的性能
int []data=new int[8000000];
for(int i=0;i<8000000;i++) {
data[i]=(int) (Math.random()*80000);
}
SimpleDateFormat format=new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
Date str01=new Date();
System.out.println("排序前--------------"+format.format(str01));
radixSort2(data);
Date str02=new Date();
System.out.println("排序后--------------"+format.format(str02));
}
由于笔记本性能较差 ,测试8000000条数据只需要1秒钟即可完成。当然机器性能越好,测试数据条数也可更多。
以上如有不妥,欢迎指出。