TypeScript实现十大排序算法(十) - 基数排序

451 阅读2分钟

一. 基数排序的定义

基数排序是一种非比较型整数排序算法,其原理是将整数按位数分组,对每一位进行桶排序。

  • 基数排序从低位向高位排序,将整数分为个位、十位、百位等不同的数位。
  • 再将每一位数位上的数字分别放入桶中进行排序,最后将排好序的数字从桶中取出。

基数排序是桶排序的扩展,用于整数排序,与桶排序相比,其多了一重循环,用于不同数位的比较。

基数排序适用于整数排序,特别是位数大小不一的整数排序,比如电话号码、银行卡号等。

基数排序的优点:

  1. 稳定性:基数排序是稳定的排序算法,排序后,相同的数字的相对位置不变。
  2. 时间复杂度低:基数排序的时间复杂度为O(n * k),k为数字的位数。
  3. 适用范围广:基数排序适用于数据范围很大的场景,如数字的位数不多。

基数排序的缺点:

  1. 需要额外的存储空间:基数排序需要使用一些额外的存储空间存储桶,占用额外的空间。
  2. 数据结构不同导致不能比较:基数排序只适用于整数数字,不能对其他数据结构进行排序。

二. 基数排序的流程

基数排序的流程分析:

  1. 根据待排序的数的位数,确定需要进行的趟数,每一趟按照对应的位数进行排序。
  2. 创建10个桶,分别代表0~9的数字,存储对应位数上数字为该数字的数。
  3. 遍历待排序数组,将每一个数字按照其对应位数上的数字放入对应的桶中。
  4. 按照桶的顺序依次遍历每一个桶,将桶中的数字存入辅助数组中。
  5. 将辅助数组的数字复制回原数组中。
  6. 如果还有多余的趟数,则从第2步开始重复上述操作,直到所有的趟数都已经结束。
  7. 排序完成。

三. 基数排序的图解

整体流程:

基数排序

四. 基数排序的代码

下面是TypeScript实现的基数排序代码,带有详细的注释:

 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;
 }
 ​

整体流程分为两步:

  1. 从低位到高位依次排序,每一位排序时都是使用计数排序的思想。
  2. 从最低位的排序结果开始,依次累加上一位的排序结果,得到最终的排序结果。

具体实现过程中,使用了一个二维数组作为桶,桶的第一维是数字的每一位,第二维是存储这一位数字相同的数字的数组。

使用了两个循环,外层循环进行排序的次数,内层循环进行元素的遍历。

最终,排序完成后,把结果存入原数组,完成排序。

五. 基数排序的时间复杂度

基数排序的时间复杂度分析:

  1. 对于单次排序,基数排序的时间复杂度为 O(n),因为每个数都只需要进行一次排序。

  2. 对于整个排序过程,基数排序的时间复杂度为 O(d(n + k)),其中 d 为位数,n 为数组长度,k 为桶的数量。

    • 在最坏情况下,当所有数的位数都相同时,d 与 n 的值相同,所以时间复杂度可以看做 O(n^2)。

基数排序的空间复杂度分析:

  1. 基数排序的空间复杂度为 O(n + k),其中 n 为数组长度,k 为桶的数量。
  2. 需要额外的数组存储排序的中间结果,空间复杂度为 O(n)。

六. 基数排序的总结

基数排序是一种非比较型整数排序算法,适用于大量数,很长的数列。它是桶排序的一种改进版本。它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。具体来说,就是将数列分别按个位,十位,百位... 的大小顺序排序,最后组合在一起。

优点:

  1. 时间复杂度稳定,为O(n * k),其中n为数列的长度,k为数列中数的最大位数。
  2. 空间复杂度小,只需要额外的常数空间。
  3. 是稳定的排序算法,也就是说,相同的数字排序后仍然相同。

缺点:

  1. 不适用于浮点数和负数。
  2. 对于数据范围较大的数列,k的大小也很大,因此,时间复杂度可能较高。

总体来说,基数排序是一种非常有效的整数排序算法,特别是对于大量数,很长的数列。

coderwhy公众号分享各种编程技术、生活、感悟,欢迎关注、交流、分享。