基数排序属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。
基数排序(Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献。
它是这样实现的: 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.
效率
基数排序的时间复杂度是 O(k·n),其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(n·log(n)),因为k的大小一般会受到 n 的影响。 以排序n个不同整数来举例,假定这些整数以B为底,这样每位数都有B个不同的数字,k就一定不小于logB(n)。由于有B个不同的数字,所以就需要B个不同的桶,在每一轮比较的时候都需要平均n·log2(B) 次比较来把整数放到合适的桶中去,所以就有:
- k 大于或等于 logB(n)
- 每一轮(平均)需要 n·log2(B) 次比较
所以,基数排序的平均时间T就是:
T ≥logB(n)·n·log2(B) = log2(n)·logB(2)·n·log2(B)= log2(n)·n·logB(2)·log2(B) = n·log2(n)
所以和比较排序相似,基数排序需要的比较次数:T ≥ n·log2(n)。故其时间复杂度为Ω(n·log2(n)) = Ω(n·log n) 。
时间效率:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(n),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针。
实现的方法
最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
基数排序的缺点:
不呈现时空局部性,因为在按位对每个数进行排序的过程中,一个数的位置可能发生巨大的变化,所以不能充分利用现代机器缓存提供的优势。同时计数排序作为中间稳定排序的话,不具有原地排序的特点,当内存容量比较宝贵的时候,还是有待商榷。
- package com.algorithm.sort;
- import java.util.Arrays;
- public class RadixSort {
- //基于计数排序的基数排序算法
- private static void radixSort(int[] array,int radix, int distance) {
- //array为待排序数组
- //radix,代表基数
- //代表排序元素的位数
- int length = array.length;
- int[] temp = new int[length];//用于暂存元素
- int[] count = new int[radix];//用于计数排序
- int divide = 1;
- for (int i = 0; i < distance; i++) {
- System.arraycopy(array, 0,temp, 0, length);
- Arrays.fill(count, 0);
- for (int j = 0; j < length; j++) {
- int tempKey = (temp[j]/divide)%radix;
- count[tempKey]++;
- }
- for (int j = 1; j < radix; j++) {
- count [j] = count[j] + count[j-1];
- }
- //个人觉的运用计数排序实现计数排序的重点在下面这个方法
- for (int j = length - 1; j >= 0; j--) {
- int tempKey = (temp[j]/divide)%radix;
- count[tempKey]--;
- array[count[tempKey]] = temp[j];
- }
- divide = divide * radix;
- }
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- int[] array = {3,2,3,2,5,333,45566,2345678,78,990,12,432,56};
- radixSort(array,10,7);
- for (int i = 0; i < array.length; i++) {
- System.out.print(" " + array[i]);
- }
- }
- }