基数排序是一种借助多关键字排序思想对单逻辑关键字进行排序的方法。基础排序不是基于关键字比较的排序算法,它适用于元素很多而关键字较少的序列,基数的选择和关键字的分解是根据关键字的类型来决定的,例如:关键字是十进制数字,按照各位、十位来分解
在此之前我们需要了解另外两种排序方式计数排序和桶排序
计数排序
此排序对于数列元素较小,一般为100以内的数据,极快 时间复杂度可以几乎为O(n),但是极其耗费空间
类似于‘有钱任性’
* 基本的原理:
* 将待排序的数组中最大的值MAX记录下来,然后创建一个新的数组且数组的大小为MAX+1
* 将新的数组初始化值都为0;然后遍历待排序的数组--此时待排序的数组的所有元素都势必在新数组的索引覆盖之下
* 即在新数组中总能找到一个位置(索引)来对应待排序数组的元素值
* 遍历是待排序的数组元素值出现一次,就在新数组的相应索引位置的key +1以记录其值出现的次数(重复的值的个数)
* 然后遍历新的数组,当然新数组中大量的索引位置的值都是0;所以只需要输出索引位置不是0的索引值,,是索引值!
* 索引本身是有序的。。。
* 然后索引值对应的数组值为多少就输出多少个。。最后排好序的数组就出来了 有不有
* 就是这么霸道!!!
* 然而:
* 因为是借助索引的值,所以其排序的对象只能是自然数(0和正整数);
* 负数就不说了
* 其实有时候我们也需要排序一个对象(安对象的某个属性排序),利用java8的lambda表达式和comparator接口可以快速的实现
public static void count(int []a){
int max = getMax(a);
int []arr = new int[max+1];//新数组
for(int i = 0; i < a.length;i++){
arr[a[i]] = arr[a[i]]+1;
}
int k = 0;
//重新赋值给原数组_已经排好序
for(int i = 0;i < arr.length;i++){
while(arr[i]-- > 0){
a[k++] = i;
}
}
}
//计算数组中最大的元素值
public static int getMax(int []a){
int max = 0;
for(int i : a){
if(i > max){
max = i;
}
}
return max;
}
桶排序
* 桶排序是计数排序的升级版
* 时间复杂度 O(N+C),其中C=N*(logN-logM)。当N=M桶内只有一个元素时,可达到O(N)
* 如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。
* 当然桶排序的空间复杂度 为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。
* 此外,桶排序是稳定的。
* ==将大量数据分割处理然后合并===类似于分布式系统设计和集群处理
* 基本原理:
* 初始化10个桶,将待排序数组安最高位的值放入桶中。然后在各桶中分别采取排序
* (可根据具体情况初始化桶的个数和放入桶中数据的放入算法)
* 然后输出或安计数排序方式赋值给元素数组即可得到排好序的数组
* 缓解了计数排序大量的空间消耗--用部分时间换取了部分空间
* 另外桶排序需要借助出一维数组外的其他数据结构
public static void bucket(int []a){
//初始化10个桶--此处用list实现,因不知道每个桶中元素的个数以及限制,暂时没想到怎么用二维数组实现
List<LinkedList<Integer>> bucket = new LinkedList<LinkedList<Integer>>();//前:桶的编号 后:桶中的元素
for(int i = 0; i < 10; i++){
bucket.add(new LinkedList<Integer>());
}
//去数组中最大元素的位数--做高位取值入桶
int digit = getdigit(a);
//放入对应的桶中
for(int i=0;i<a.length;i++){
int k = a[i]/(10*(digit-1));//桶的编号
bucket.get(k).add(a[i]);
}
//对桶中的数据排序
for(int i = 0; i<10;i++){
if(!bucket.get(i).isEmpty()){
Collections.sort(bucket.get(i));
}
}
//复制桶中有序的元素到,待排序的数组中
for(int i = 0,c = 0;i < 10;i++){
if(!bucket.get(i).isEmpty()){
for(int j = 0; j < bucket.get(i).size();j++){
a[c] = bucket.get(i).get(j);
c++;
}
}
}
}
public static int getdigit(int a[]){
int max = 0;
for(int i : a){
if(i > max){
max = i;
}
}
return String.valueOf(max).length();
}
基数排序 时间复杂度为O(d * n) d为数列最大元素的位数 n是元素的个数
* --相当于将计数排序集群分析处理
* --其结合了计数排序和桶排序的优点
* --至于网上大多数所描述的时间复杂度为O(n*log(r)m或O(d(n+radix)),可能采取的算法有一定的出入也没有明确的算法,
* 以上时间复杂度与一下源代码的比较更为直接和准确
* --此排序是从桶排序改进而来时间复杂度也有相识之处
* 一趟收集时间复杂度为O(n + r),共进行d趟分配和收集
* 桶排序虽然缓解了计数排序的空间问题,单会产生比较-数据的交换-性能并不会得到明显的提升
* 而基数排序可以实现无比较(数据的交换)
* 桶排序按最高位放入桶中之后,其实也相当于做了一点排序操作,不难发现,经过放入桶的操作,
* 至少可以安最高位分出最高位的数据的顺序
* 那么如果在分出高位顺序的前提下在按较低位放入桶中?
* 仔细想一下,毫无作用:高位比较大小后低位的比较毫无价值-可是如果从低位比到高位?
* 按低位放入桶会影响到先前高位的顺序;而 按高位放入桶中不会影响先前低位的顺序
* 基数排序就是按照从低到高的位值放入桶再依次拿出来再放入再拿出来最后到最高位
* 到个位的时候自然就实现了完整的排序
* 可以省去桶排序的比较过程,即桶内的排序
* 而只是修改一部分代码
public static void redix(int []a , int bit){
//最高位的时候结束--getdigit(int []a)取得数组元素的最大位数
if(bit == getdigit(a)+1){//因为最高位需要走一遍所以需要加一
return ;
}
//初始化10个桶--此处用list实现,因不知道每个桶中元素的个数以及限制,暂时没想到怎么用二维数组实现
List<LinkedList<Integer>> bucket = new LinkedList<LinkedList<Integer>>();//前:桶的编号 后:桶中的元素
for(int i = 0; i < 10; i++){
bucket.add(new LinkedList<Integer>());
}
//放入对应的桶中
for(int i=0;i<a.length;i++){
//比较的位数的取模的位
int v ;
int k;//桶的编号
if(bit != 1){
v = 10*(bit - 1);
k = a[i]/v%10;
}else{
k = a[i]%10;
}
bucket.get(k).add(a[i]);
}
//复制桶中有序的元素到,待排序的数组中
for(int i = 0,c = 0;i < 10;i++){
if(!bucket.get(i).isEmpty()){
for(int j = 0; j < bucket.get(i).size();j++){
a[c] = bucket.get(i).get(j);
c++;
}
}
}
redix(a,bit+1);//递归
}