基数排序
6.1 思路
基数排序是一种非比较型整数排序算法,其原理是将整数按位数分组,对每一位进行桶排序。
- 基数排序从低位向高位排序,将整数分为个位、十位、百位等不同的数位。
- 再将每一位数位上的数字分别放入桶中进行排序,最后将排好序的数字从桶中取出。
基数排序是桶排序的扩展,用于整数排序,与桶排序相比,其多了一重循环,用于不同数位的比较。
基数排序适用于整数排序,特别是位数大小不一的整数排序,比如电话号码、银行卡号等。
基数排序的优点:
- 稳定性:基数排序是稳定的排序算法,排序后,相同的数字的相对位置不变。
- 时间复杂度低:基数排序的时间复杂度为O(n * k),k为数字的位数。
- 适用范围广:基数排序适用于数据范围很大的场景,如数字的位数不多。
基数排序的缺点:
- 需要额外的存储空间:基数排序需要使用一些额外的存储空间存储桶,占用额外的空间。
- 数据结构不同导致不能比较:基数排序只适用于整数数字,不能对其他数据结构进行排序。
6.2 动图展示
6.3 基数排序流程
- 根据待排序的数的位数,确定需要进行的趟数,每一趟按照对应的位数进行排序。
- 创建10个桶,分别代表0~9的数字,存储对应位数上数字为该数字的数。
- 遍历待排序数组,将每一个数字按照其对应位数上的数字放入对应的桶中。
- 按照桶的顺序依次遍历每一个桶,将桶中的数字存入辅助数组中。
- 将辅助数组的数字复制回原数组中。
- 如果还有多余的趟数,则从第2步开始重复上述操作,直到所有的趟数都已经结束。
- 排序完成。
6.4 基数排序代码
function radixSort(arr: number[]) {
const maxDigit = getMaxDigit(arr); // 获取最大位数
for (let i = 0; i < maxDigit; i++) {
let buckets: number[][] = []; // 创建桶
for (let j = 0; j < arr.length; j++) {
let digit = getDigit(arr[j], i); // 获取数字的第i位数字
if (!buckets[digit]) {
buckets[digit] = [];
}
buckets[digit].push(arr[j]); // 将数字放入相应的桶中
}
arr = [].concat(...buckets); // 将桶中的数字取出来,重新放入arr数组中
}
return arr;
}
// 获取最大位数
function getMaxDigit(arr: number[]) {
let max = 0;
for (let i = 0; i < arr.length; i++) {
let digit = getDigitCount(arr[i]);
max = Math.max(max, digit);
}
return max;
}
// 获取数字的位数
function getDigitCount(num: number) {
if (num === 0) return 1;
return Math.floor(Math.log10(Math.abs(num))) + 1;
}
// 获取数字的第i位数字
function getDigit(num: number, i: number) {
return Math.floor(Math.abs(num) / Math.pow(10, i)) % 10;
}
整体流程分为两步:
- 从低位到高位依次排序,每一位排序时都是使用计数排序的思想。
- 从最低位的排序结果开始,依次累加上一位的排序结果,得到最终的排序结果。
具体实现过程中,使用了一个二维数组作为桶,桶的第一维是数字的每一位,第二维是存储这一位数字相同的数字的数组。
使用了两个循环,外层循环进行排序的次数,内层循环进行元素的遍历。
最终,排序完成后,把结果存入原数组,完成排序。
6.5 基数排序的时间复杂度
基数排序的时间复杂度分析:
- 对于单次排序,基数排序的时间复杂度为 O(n),因为每个数都只需要进行一次排序。
- 对于整个排序过程,基数排序的时间复杂度为 O(d(n + k)),其中 d 为位数,n 为数组长度,k 为桶的数量。
-
- 在最坏情况下,当所有数的位数都相同时,d 与 n 的值相同,所以时间复杂度可以看做 O(n^2)。
基数排序的空间复杂度分析:
- 基数排序的空间复杂度为 O(n + k),其中 n 为数组长度,k 为桶的数量。
- 需要额外的数组存储排序的中间结果,空间复杂度为 O(n)。
6.6 基数排序小结
基数排序是一种非比较型整数排序算法,适用于大量数,很长的数列。它是桶排序的一种改进版本。它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。具体来说,就是将数列分别按个位,十位,百位... 的大小顺序排序,最后组合在一起。
优点:
- 时间复杂度稳定,为O(n * k),其中n为数列的长度,k为数列中数的最大位数。
- 空间复杂度小,只需要额外的常数空间。
- 是稳定的排序算法,也就是说,相同的数字排序后仍然相同。
缺点:
- 不适用于浮点数和负数。
- 对于数据范围较大的数列,k的大小也很大,因此,时间复杂度可能较高。
总体来说,基数排序是一种非常有效的整数排序算法,特别是对于大量数,很长的数列。