基数排序算法
·
基数排序属于"分配式排序",又称"桶子法",它是一种借助多关键字的思想对单逻辑关键字进行排序的算法。
基数排序有两种方法:
1.最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
2.最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
因为LSD更符合大家的思维方式,下面我以LSD为例讲解:
假设初始序列为:Array{3,1,13,24,36,23,21,44,25,19}
任何一个整数各个位上的基数都可以0~9来表示,我们不妨把0~9视为10个桶。
我们先根据序列的个位数的数字来进行分类,将其分到指定的桶中。例如:Array[0] = 3,个位数上是3,将这个数存入编号为3的桶中。
| [0] | |||
| [1] | 1 | 21 | |
| [2] | |||
| [3] | 3 | 13 | 23 |
| [4] | 24 | 44 | |
| [5] | 25 | ||
| [6] | 36 | ||
| [7] | |||
| [8] | |||
| [9] | 19 |
分类后,我们在从各个桶中,将这些数按照从编号0到编号9的顺序依次将所有数取出来。
这时,得到的序列就是个位数上呈递增趋势的序列。
按照个位数排序: {1, 21, 3, 13, 23, 24, 44, 25, 36, 19}。
接下来,对十位数也按照这种方法进行排序。
| [0] | 1 | 3 | ||
| [1] | 13 | 19 | ||
| [2] | 21 | 23 | 24 | 25 |
| [3] | 36 | |||
| [4] | 44 | |||
| [5] | ||||
| [6] | ||||
| [7] | ||||
| [8] | ||||
| [9] |
这是得到序列:{1,3, 13,19,21,23,24,25,36, 44}。
也可以从下图中看出基数排序的过程:
下面是代码实现:
int getMaxBit(int *arr, int length) //辅助函数,求数据的最大位数
{
int bitNum = 1; //保存最大的位数
int p = 10;
for (int i = 0; i < length; ++i)
{
while (arr[i] >= p)
{
p *= 10;
++bitNum;
}
}
return bitNum;
}
void RadixSort(int *arr, int length)//基数排序
{
int bitNum = getMaxBit(arr, length);
int *tmp = new int[length];
int *count = new int[10];//计数器
int i, j, k;
int radix = 1;
for (i = 1; i <= bitNum; i++)//进行bitNum次排序
{
for (j = 0; j < 10; j++)
count[j] = 0;//每次分配前清空计数器
for (j = 0; j < length; j++)
{
k = (arr[j] / radix) % 10;//统计每个桶中的记录数
count[k]++;
}
for (j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j];
// 这里要从右向左扫描,保证排序稳定性
for (j = length - 1; j >= 0; j--)//将所有桶中记录依次收集到tmp中
{
k = (arr[j] / radix) % 10;
tmp[count[k] - 1] = arr[j];
count[k]--;
}
for (j = 0; j < length; j++)
arr[j] = tmp[j];
radix *= 10;
}
delete[] tmp;
delete[] count;
}这里写的是最低位优先法,最高位优先法是从最高位开始放入桶中,然后将每个桶中的数再次放入另外的桶中,不断重复,直到每个桶中只有一个元素,可以通过迭代实现。基数排序的性能
|
排序类别 |
排序方法 |
时间复杂度 |
空间复杂度 |
稳定性 |
复杂性 | ||
|
平均情况 |
最坏情况 |
最好情况 | |||||
|
基数排序 |
基数排序 |
O(d(n+r)) |
O(d(n+r)) |
O(d(n+r)) |
O(n+r) |
稳定 |
较复杂 |
这里我以一千万个元素的随机数组进行测试,快排用了2.587950s,而基数排序用了3.383550s,基数排序比快排慢。但是基数排序的时间复杂度明显比快排低,这是什么原因呢?当然,随着数组元素的增加,基数排序时间呈线性增长,比如数组元素个数达到亿这个数量级的时候,基数排序慢慢就快于快速排序了。针对基数排序,还可以做很多方面的优化,比如我们可以把基数从10变为1000,将上面代码的10换为1000,相应的改变循环次数,我们会发现算法效率提高了很多。还是上面随机生成的1000万个元素的数组,只需1.322851s,速度明显提升了很多。基数最好的是选择2的幂,计算机是以二进制存储数据的,所以当采用10的幂作为基数时就会出现很多问题。
到这里,基数排序差不多也就介绍完了。算法还有很多可以改进的地方,但最要的是算法的思想,我们明白算法的原理并能够直接在脑中呈现它的模型,那么无论它怎么变,我们都可以从更高的层次把握它。
如果有什么问题,乐意和大家讨论。
另外,其他排序算法,可以参考我的另外一篇博客
“排序算法及并向析”:http://blog.csdn.net/secyb/article/details/51319391。
更多推荐
所有评论(0)