标签:java 算法 排序
根据维基百科,基数排序的定义为: 基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基数排序的思路是将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序,这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.
举个栗子:
待排序数列为:324,63,59,225,13
第一次排序(按个位大小排序):063,013,324,225,059
第二次排序(按十位大小排序):013,324,225,059,063
第三次排序(按百位大小排序):013,059,063,225,324
最终输出:13,59,63,225,324
从这个例子可以看出:下一次的排序不会影响上一次排序的结果,即当按照十位排序时如果十位上的数字相同,则它们的排序依然是上一次排序的结果(上述例子中的324与225),所以我们需要稳定排序。什么是稳定排序?
稳定排序的意思是指, 待排序相同元素之间的相对前后关系,在各次排序中不会改变.比如实例中具有十位数字2的两个数字324和225, 在十位排序之前324在225之前,在十位排序之后, 324依然在225之前。稳定排序能保证,上一次的排序成果被保留,十位数的排序过程能保留个位数的排序成果,百位数的排序过程能保留十位数的排序成果.
代码实现上述过程时我们用数组来存放数据和排序结果:
第一次排序(个位数)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
63 | 324 | 225 | 59 | ||||||
13 | |||||||||
第二次排序(十位数)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
13 | 324 | 59 | 63 | ||||||
225 | |||||||||
第三次排序(百位数)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
013 | 225 | 324 | |||||||
059 | |||||||||
063 |
最终将二维数组中的元素按列读出来就是所得结果:
13,59,63,225,324
代码实现:
package acm; /*基数排序*/ class RadxSort { public void RSort(int[] ary){ int length = ary.length; int[][] bucket = new int[length][10];//桶用来存放按位排序的数 int[] order = new int[10]; //记录一个桶中有多少个元素 int time = 1;//记录排序次数,即最大数的位数 int n = 1; int k = 0; while(time<=maxDigit(ary)){ for(int i:ary){ int digit = (i/n)%10; //从个位依次往上每位上的数 bucket[order[digit]][digit] = i; order[digit]++;//把桶中的个数加一 } for(int i=0;i<10;i++){ if(order[i]!=0){ for(int j=0;j<order[i];j++){ ary[k] = bucket[j][i];//把同一个桶中的数据全读出来,即从上往下读 k++; } } order[i] = 0; //将记录个数清零,用以下一次排序 } n*=10; k = 0; time++; } } //找出数组中最大数有多少位 int maxDigit(int[] ary){ int max=0; int n = 1; for(int i=0;i<ary.length;i++){ if(max<ary[i]) max = ary[i]; } while(max/10 != 0){ max = max/10; n++; } return n; } } public class RadixSort{ public static void main(String[] args){ int[] ary = new int[]{87,15,2,47,456,987,4561,5}; RadxSort RS = new RadxSort(); RS.RSort(ary); for(int num:ary){ System.out.print(num+","); } } }
算法复杂度分析
基数排序的时间复杂度是{\displaystyle O(k\cdot n)},其中{\displaystyle n}是排序元素个数,{\displaystyle k}是数字位数。注意这不是说这个时间复杂度一定优于{\displaystyle O\left(n\cdot \log \left(n\right)\right)},{\displaystyle k}的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小;{\displaystyle k}决定了进行多少轮处理,而{\displaystyle n}是每轮处理的操作数目。
以排序{\displaystyle n}个不同整数来举例,假定这些整数以{\displaystyle B}为底,这样每位数都有{\displaystyle B}个不同的数字,{\displaystyle k=\log _{B}N},{\displaystyle N}是待排序数据类型全集的势。虽然有{\displaystyle B}个不同的数字,需要{\displaystyle B}个不同的桶,但在每一轮处理中,判断每个待排序数据项只需要一次计算确定对应数位的值,因此在每一轮处理的时候都需要平均{\displaystyle n}次操作来把整数放到合适的桶中去,所以就有:
{\displaystyle k\approx \log _{B}N}
所以,基数排序的平均时间{\displaystyle T}就是:
{\displaystyle T\approx \log _{B}\left(N\right)\cdot n}
其中前一项是一个与输入数据无关的常数,当然该项不一定小于{\displaystyle \log n}。
如果考虑和比较排序进行对照,基数排序的形式复杂度虽然不一定更小,但由于不进行比较,因此其基本操作的代价较小,而且在适当选择的{\displaystyle B}之下,{\displaystyle k}一般不大于{\displaystyle \log n},所以基数排序一般要快过基于比较的排序,比如快速排序。
本文出自 “卫莨” 博客,请务必保留此出处http://acevi.blog.51cto.com/13261784/1982167
标签:java 算法 排序
原文地址:http://acevi.blog.51cto.com/13261784/1982167