基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
算法描述
- 取得数组中的最大数,并取得位数;
- arr为原始数组,从最低位开始取每个位组成radix数组;
- 对radix进行计数排序(利用计数排序适用于小范围数的特点);
动图演示
代码实现
let array = randomArray(1, 120);
console.log(array);
radixSort(array);
console.log(array);
function radixSort(arr) {
//定义一个二维数组,表示10个桶,每个桶就是一个一维数组
//说明
//1,二维数组包含10个一维数组,
//2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶)
//大小定为arr.length
//3.很明确,基数排序是使用空间换时间的经典算法
let bucket = new Array(10);
for (let i = 0; i < bucket.length; i++) {
bucket[i] = new Array(arr.length);
}
//为了记录每个桶中,实际存了多少个数据,我们定义一个
//一维数组来记录每个桶的每次放入的数据个数
//可以这里理解
//比如:bucketElementCounts[0],记录的就是bucket[0]桶的放入数据个数
let buckeElementCounts = new Array(10).fill(0);
//1.得到数组中最大的位数
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
//得到最大是几位数
let maxLength = (max + '').length;
for (let i = 0, n = 1; i < maxLength; i++, n = n * 10) {
//每一轮,对每个元素的各个位数进行排序处理,
//第一次是个位,第二次是十位,第三次是百位
for (let j = 0; j < arr.length; j++) {
//取出每个元素的各位的值
let digitOfElement = Math.floor(arr[j] / n) % 10;
bucket[digitOfElement][buckeElementCounts[digitOfElement]] = arr[j];
buckeElementCounts[digitOfElement]++;
}
//按照这个桶的顺序(以为数组的下标依次取出数据,放入原来数组)
let index = 0;
//遍历每一桶,并将桶中的数据,放入原数组
for (let k = 0; k < buckeElementCounts.length; k++) {
//如果桶中有数据,我们才放入原数组
if (buckeElementCounts[k] !== 0) {
//循环该桶即第k个桶,即第k个一维数组,放入
for (let l = 0; l < buckeElementCounts[k]; l++) {
//取出元素放入arr
arr[index] = bucket[k][l];
//arr下标后移
index++;
}
//每轮处理后,下标要清0
buckeElementCounts[k] = 0;
}
}
}
}
打印结果
算法分析
基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。
基数排序的空间复杂度为O(n+k),其中k为桶的数量。一般来说n>>k,因此额外空间需要大概n个左右。