基数排序
基本思想
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或binsort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
操作方法
(1)假设有欲排数据序列如下所示:73 22 93 43 55 14 28 65 39 81首先根据个位数的数值,在遍历数据时将它们各自分配到编号0至9的桶(个位数值与桶号一一对应)中。分配结果(逻辑想象)如下图所示:
(2)分配结束后。接下来将所有桶中所盛数据按照桶号由小到大(桶中由顶至底)依次重新收集串起来,得到如下仍然无序的数据序列:
81 22 73 93 43 14 55 65 28 39
(3)接着,再进行一次分配,这次根据十位数值来分配(原理同上),分配结果(逻辑想象)如下图所示:
算法实现(Java)
// 基数排序的实现,有两种方式,低位优先法,适用于位数较小的数排序 简称LSD
// 高位优先法,适用于位数较多的情况,简称MSD
// 这里实现低位优先法
public static void LSDSort(int a[]) {
int max = a[0];
for (int i = 1; i < a.length; i++) {
if (a[i] > max)
max = a[i];
}
int len = 0; // 存贮最大的数的位数,用来判断需要进行几轮基数排序
while (max > 0) {
max = max / 10;
len++;
}
for (int times = 0; times < len; times++) // 按位数运行几次,每次都分裂成10份,在顺序链接
{
// 以下内容应为每次运行时,分割成0-9 个桶,然后顺序链接
@SuppressWarnings("unchecked")
Vector<Integer> num[] = new Vector[10];
for (int i = 0; i < num.length; i++) {
num[i] = new Vector<Integer>();
}
int k = 1; // 用来取出当前的对应的位数的数
for (int i = 0; i < times; i++) {
k = k * 10;
}
for (int i = 0; i < a.length; i++) // 对每一个数进行判断位数
{
int x = 0; // 用来表示当前的位数上的数的大小
x = Math.abs(a[i]) / k; // 使用绝对值,防止受到正负号的影响
x = x % 10;
num[x].add(new Integer(a[i]));
}
// 将排序的结果顺序连接起来
int count = 0;
for (int i = 0; i < num.length; i++) {
for (int j = 0; j < num[i].size(); j++) {
a[count++] = num[i].get(j).intValue();
}
}
}
// 判断完绝对值大小顺序后,需要判断正负了
Vector<Integer> plus = new Vector<Integer>(); // 储存正数
Vector<Integer> minus = new Vector<Integer>(); // 储存负数
for (int i = 0; i < a.length; i++) {
if (a[i] <= 0) {
minus.add(new Integer(a[i]));
} else {
plus.add(new Integer(a[i]));
}
}
int count = 0;
for (int i = minus.size() - 1; i > 0; i--) // 因为这是按照绝对值大小排列的,所以要倒序
{
a[count++] = minus.get(i);
}
for (int i = 0; i < plus.size(); i++) {
a[count++] = plus.get(i);
}
}
效率分析
算法性能:
时间复杂度:
在基数排序中,r为基数,d为位数。则基数排序的时间复杂度为O(d(n+r))。
我们可以看出,基数排序的效率和初始序列是否有序没有关联。
空间复杂度
对于任何位数上的基数进行“装桶”操作时,都需要n+r个临时空间。
算法稳定性
在基数排序过程中,每次都是将当前位数上相同数值的元素统一“装桶”,并不需要交换位置。所以基数排序是稳定的算法。