作者前言
大家好,我是阿濠,今篇内容跟大家分享的是排序算法之基数排序,很高兴分享到segmentfault与大家一起学习交流,初次见面请大家多多关照,一起学习进步.
一、基数排序的介绍
基本介绍
1.基数排序(radixsort)
属于“分配式排序
”(distributionsort) ,又称“桶子法
”( bucket sort)或binsort, 顾名思义,它是通过键值的各个位的值
,将要排序的元素分配
至某些“桶”中
,达到排序
的作用
2.基数排序法
是属于稳定性的排序
,基数排序法的是效率高的稳定性排序法
3.基数排序(Radix Sort)
是桶排序的扩展
4.基数排序是1887年赫尔曼何乐礼
发明的。它是这样实现的:将整数按位数切割成不同的数字
,然后按每个位数分别比较
。
基本思想
1.第1轮
将待排序
的每个元素个位数取出
,依次放入对应的桶里
,并从桶依次取出数据
放入原来的数组
2.第2轮
将待排序
的每个元素十位数取出
,依次放入对应的桶里
,并从桶依次取出数据
放入原来的数组
3.第n轮
将待排序
的元素按每个位数分别比较
,并从桶依次取出数据
放入原来的数组
,以此类推
4.按照桶的顺序
,依次取出数据
,放入原来的数组里
二、通过应用示例认识基数排序
有一群小牛,售价分别是{53,3,542,748,14,214}
,请从小到大
使用基数排序
,进行排序
温馨提示:
1.获取位数:个位%10、十位 /10 %10 、百位/100 %10
...
2.本次步骤思路与代码未考虑负数情况
图解步骤思路与实现代码:
1.第1轮
将待排序
的每个元素个位数取出
,然后依次放入对应的桶里
,按照桶的顺序
依次取出放入原来的数组
:arr={542,53,3,14,214,748}
//第1轮(针对每个元素的个位进行排序处理)
//定义一个二维数组,表示10个桶,每个桶就是一个一维数组
//说明
//1.二维数组包含10个一维数组
//2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.1ength
//3.名明确, 基数排序是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的放入的数据个数
//可以这里理解
//比如: bucketElementCounts[0],记录的就是bucket[0] 桶的放入数据个数
//比如:bucket[0] 第一个桶放入数字:40、30 那么bucketElementCounts[0]=2
int[] bucketElementCounts = new int[10];
//第1轮(针对每个元素的个位进行排序处理)
for(int j = 0; j < arr.length; j++) {
//取出每个元素的个位的值
//比如说放入第一个数字:45
//45 /10 % 10 =5
int digitOfElement = arr[j] /10 % 10;
//放入到对应的桶中 按图所示应该是数字为5 的桶
//按照bucket 数组来算 则是bucket[5][0] 是第六个桶第一个数
//bucketElementCounts[5]对应bucket[5]
//此时bucket[5][0]存入一个45 则bucketElementCounts[5]=1记录下来
bucket[digitOfElement][bucketElementCounts[digitOfElement]]=arr[j];
//bucketElementCounts[5] 记录下来
bucketElementCounts[digitOfElement]++;
}
System.out.println("此时各个桶的放入的数据个数:"+ Arrays.toString(bucketElementCounts));
//按照桶的顺序(一维数组的下标依次取出数据,放入原来的数组)
//原来的数组:arr[]={ 53, 3, 542, 748 , 14, 214};
//放入原数组从下标0开始;
int index=0;
//遍历每一桶,并将桶中是数据,放入到原数组
//此时bucketElementCounts对应的下标记录的是有效个数
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
//因为bucketElementCounts对应的下标记录的是有效个数
//如果对应的桶中没有数据则无需取出数据
//比如说放入第一个数字:45 第一轮取个数 45 /10 % 10
//按照bucket数组来则是bucket[5][0] 第六个桶第一个数
//bucketElementCounts[5]对应bucket[5] bucketElementCounts[5]=1
//此时取出数据则只需判断 bucketElementCounts[5]!=0 即可
if (bucketElementCounts[k] != 0) {
//比如说取出45 则只需要找到bucket[5][0]即可
//循环该桶即第k个桶(即第k个一维数组),放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
//bucketElementCounts[5]=1 对应的是bucket[5][0]
//所以k=5时 l=0时则代表找到刚刚放入的数字:45
//放入原数组的第一个位置下标=0
//放入成功后进行++ 对下一个位置进行处理
arr[index++] = bucket[k][l];
}
}
//第一轮处理完后需要将每个bucketElementCounts[k] =0
//方便第二轮进行下一次统计
bucketElementCounts[k]=0;
}
System.out.println("第一轮,对个数进行排序处理,此时arr="+Arrays.toString(arr));
运行结果如下:
此时各个桶的放入的数据个数:[0, 0, 1, 2, 2, 0, 0, 0, 1, 0]
第一轮,对个数进行排序处理,此时arr=[542, 53, 3, 14, 214, 748]
2.第2轮
将待排序
的每个元素十位数取出
,然后依次放入对应的桶里
,按照桶的顺序
依次取出放入原来的数组
:arr={3,14,214,542,748,53}
//第2轮(针对每个元素的十位进行排序处理)
for(int j = 0; j < arr.length; j++) {
//取出每个元素的十位的值
//比如说放入第二个数字:65
//65 % /10 % 10 = 6
int digitOfElement = arr[j] /10 % 10;
//放入到对应的桶中 按图所示应该是数字为6 的桶
//按照bucket 数组来算 则是bucket[6][0] 是第七个桶第一个数
//bucketElementCounts[6]对应bucket[6]
//此时bucket[6][0]存入一个65 则bucketElementCounts[6]=1记录下来
bucket[digitOfElement][bucketElementCounts[digitOfElement]]=arr[j];
//bucketElementCounts[6] 记录下来
bucketElementCounts[digitOfElement]++;
}
System.out.println("此时各个桶的放入的数据个数:"+ Arrays.toString(bucketElementCounts));
//按照桶的顺序(一维数组的下标依次取出数据,放入原来的数组)
//放入原数组从下标0开始;
index=0;
//遍历每一桶,并将桶中是数据,放入到原数组
//此时bucketElementCounts对应的下标记录的是有效个数
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
//因为bucketElementCounts对应的下标记录的是有效个数
//如果对应的桶中没有数据则无需取出数据
//比如说放入第二个数字:63 第二轮取十数 63 / 10 %10
//按照bucket数组来则是bucket[6][0] 第七个桶第一个个数
//bucketElementCounts[6]对应bucket[6] bucketElementCounts[6]=1
//此时取出数据则只需判断 bucketElementCounts[6]!=0 即可
if (bucketElementCounts[k] != 0) {
//比如说取出65 则只需要找到bucket[6][0]即可
//循环该桶即第k个桶(即第k个一维数组),放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
//bucketElementCounts[6]=1 对应的是bucket[6][0]
//所以k=6时 l=0时则代表找到刚刚放入的数字:65
//放入原数组的第一个位置下标=0
//放入成功后进行++ 对下一个位置进行处理
arr[index++] = bucket[k][l];
}
}
//第二轮处理完后需要将每个bucketElementCounts[k] =0
//方便第三轮进行下一次统计
bucketElementCounts[k]=0;
}
System.out.println("第二轮,对个数进行排序处理,此时arr="+Arrays.toString(arr));
运行结果如下:
此时各个桶的放入的数据个数:[1, 2, 0, 0, 2, 1, 0, 0, 0, 0]
第二轮,对个数进行排序处理,此时arr=[3, 14, 214, 542, 748, 53]
3.第3轮
将待排序
的每个元素百位数取出
,然后依次放入对应的桶里
,按照桶的顺序
依次取出放入原来的数组
:arr={3,14,53,214,542,748}
//第3轮(针对每个元素的百位进行排序处理)
for(int j = 0; j < arr.length; j++) {
//取出每个元素的十位的值
//比如说放入第一个数字:145
//145 % /100 % 10 = 1
int digitOfElement = arr[j] /100 % 10;
//放入到对应的桶中 按图所示应该是数字为1 的桶
//按照bucket 数组来算 则是bucket[1][0] 是第二个桶第一个数
//bucketElementCounts[1]对应bucket[1]
//此时bucket[1][0]存入一个145 则bucketElementCounts[1]=1记录下来
bucket[digitOfElement][bucketElementCounts[digitOfElement]]=arr[j];
//bucketElementCounts[1] 记录下来
bucketElementCounts[digitOfElement]++;
}
System.out.println("此时各个桶的放入的数据个数:"+ Arrays.toString(bucketElementCounts));
//按照桶的顺序(一维数组的下标依次取出数据,放入原来的数组)
//放入原数组从下标0开始;
index=0;
//遍历每一桶,并将桶中是数据,放入到原数组
//此时bucketElementCounts对应的下标记录的是有效个数
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
//因为bucketElementCounts对应的下标记录的是有效个数
//如果对应的桶中没有数据则无需取出数据
//比如说放入第一个数字:145 第三轮取百数 145 /100 % 10
//按照bucket数组来则是bucket[1][0] 第二个桶第一个个数
//bucketElementCounts[1]对应bucket[1] bucketElementCounts[1]=1
//此时取出数据则只需判断 bucketElementCounts[1]!=0 即可
if (bucketElementCounts[k] != 0) {
//比如说取出33 则只需要找到bucket[1][0]即可
//循环该桶即第k个桶(即第k个一维数组),放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
//bucketElementCounts[1]=1 对应的是bucket[1][0]
//所以k=1时 l=0时则代表找到刚刚放入的数字:145
//放入原数组的第一个位置下标=0
//放入成功后进行++ 对下一个位置进行处理
arr[index++] = bucket[k][l];
}
}
//第三轮处理完后需要将每个bucketElementCounts[k] =0
//方便进行下一次统计
bucketElementCounts[k]=0;
}
System.out.println("第三轮,对个数进行排序处理,此时arr="+Arrays.toString(arr));
运行结果如下:
此时各个桶的放入的数据个数:[3, 0, 1, 0, 0, 1, 0, 1, 0, 0]
第三轮,对个数进行排序处理,此时arr=[3, 14, 53, 214, 542, 748]
规律已经出来了,与整合数组与最大数有关
、每次取%与十的整数倍相关
,根据推导进行代码抽整
//基数排序方法
public static void radixSort(int[] arr) {
//根据前面的推到过程,获取最终的基数排序代码
//一.获取数组中最大数的位数
int max = arr[0];//假设数组第一位是最大数
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//直接获取位数,但此方法不适合负数
int maxLength = (max + "").length();
//二、定义一个二维数组,表示10个桶,每个桶就是一个一维数组
//说明
//1.二维数组包含10个一维数组
//2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.1ength
//3.名明确, 基数排序是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的放入的数据个数
//可以这里理解
//比如: bucketElementCounts[0],记录的就是bucket[0] 桶的放入数据个数
//比如:bucket[0] 第一个桶放入数字:40、30 那么bucketElementCounts[0]=2
int[] bucketElementCounts = new int[10];
//三、根据最大位进行循环
//比如说数组 arr={ 53, 3, 542, 748 , 14, 214};
//最大数是748 最大位是3 分别要进行个位、十位、百位 3次循环
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;
//放入到对应的桶中并bucketElementCounts进行对应的记录
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
System.out.println("此时各个桶的放入的数据个数:"+ Arrays.toString(bucketElementCounts));
int index=0;
//遍历每一桶,并将桶中是数据,放入到原数组
//此时bucketElementCounts对应的下标记录的是有效个数
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
//因为bucketElementCounts对应的下标记录的是有效个数
//如果对应的桶中没有数据则无需取出数据
//此时取出数据则只需判断!=0 即可
if (bucketElementCounts[k] != 0) {
//循环该桶即第k个桶(即第k个一维数组),放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
//放入原数组的第一个位置下标=0
//放入成功后进行++ 对下一个位置进行处理
arr[index++] = bucket[k][l];
}
}
//第n轮处理完后需要将每个bucketElementCounts[k] =0
//方便第n+1轮进行下一次统计
bucketElementCounts[k]=0;
}
System.out.println("第"+(i+1)+"轮,对个数进行排序处理,此时arr="+Arrays.toString(arr));
}
}
三、基数排序说明
1.基数排序是对传统桶排序的扩展
,速度很快
.
2.基数排序是经典的空间换时间的方式
,占用内存很大
,当对海量数据排序
时,容易造成QutQfMemoryError(内存不足)
。
3.基数排序是稳定的
。
假定在待排序的记录序列
中,存在多个具有相同的关键字
的记录,若经过排序
,这些记录的相对次序保持不变
,即在原序列中
,r[i]=r[j]
, 且r[i]在r[j]之前
,而在排序后的序列
中,r[i]仍存在r[j]之前
,则称这种排序算法是稳定的;否则称为不稳定的
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。