![8b77725434a82140d925df6e259fec30.png](https://i-blog.csdnimg.cn/blog_migrate/1345ec5e47b3161c13343e160591f891.jpeg)
之前的几篇文章已经给大家讲解了8种排序算法的原理以及C++代码实现方式,今天带大家快速了解一下基数排序算法。
9.基数排序(Radix Sort)
基数排序(radix sort)属于“分配式排序”(distribution sort),是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序法是属于稳定性的排序。
取得数组中的最大数,并取得位数;
arr为原始数组,从最低位开始取每个位组成radix数组;
对radix进行计数排序(利用计数排序适用于小范围数的特点);
9.2 动图演示
- 先以个位数为基数进行入桶操作,然后再出桶,此时数据还是乱序的
- 再以十位数为基数进行入桶操作,然后再出桶,此时数据已经排序完成
![cd8933e748ded2c80ee5f74821ba8c7e.gif](https://i-blog.csdnimg.cn/blog_migrate/7acbf568831965180991b3cffc5da581.gif)
代码实现:
//找出最大数,获得它的位数int getMaxBit(int arr[], int n) {int max = arr[0];for (int i = 1; i < n; i++){if (arr[i] > max)max = arr[i];}int bit = 1;while (max >= 10){max /= 10;bit++;}return bit;}void radixSort(int arr[], int n){int bit = getMaxBit(arr, n);int *tmp = new int[n];int *count = new int[10]; //计数器int i, j, k;int radix = 1;for (i = 1; i <= bit; i++) //进行bit次排序{//每次分配前清空计数器for (j = 0; j < 10; j++)count[j] = 0;//统计每个桶中的记录数for (j = 0; j < n; j++){k = (arr[j] / radix) % 10;count[k]++;}//记录每个基数的当前可用的第一个位置,比如基数为0的有2个,基数为1的有3个,则基数为0的第一个可用位置是0,基数为//1的第一个可用位置为2,因为前面已经有了两个基数为0的数,基数为1的第二个可用位置为3,以此类推int pos = count[0];int temp;for (j = 1; j < 10; j++){temp = count[j];count[j] = pos;pos += temp;}count[0] = 0;//将所有桶中记录依次收集到tmp中for (j = 0; j < n; j++){k = (arr[j] / radix) % 10; //计算arr[j]的基数tmp[count[k]] = arr[j]; //将arr[j]放入基数为k的第一个可用位置count[k]++; //基数为k的第一个可用位置往后移一位}//将临时数组的内容复制到arr中for (j = 0; j < n; j++)arr[j] = tmp[j];radix = radix * 10;}delete[]tmp;delete[]count;}
其中要注意更改count数组的这一段代码,是我自己喜欢这样写,我觉得更符合常规的思维:
![25792cc5f9dba8574113125907762e9c.png](https://i-blog.csdnimg.cn/blog_migrate/844d52a9fdf728310039a6f8cdb6fc31.jpeg)
//记录每个基数的当前可用的第一个位置,比如基数为0的有2个,基数为1的有3个,则基数为0的第一个可用位置是0,基数为//1的第一个可用位置为2,因为前面已经有了两个基数为0的数,基数为1的第二个可用位置为3,以此类推int pos = c[0];int temp;for (j = 1; j < 10; j++){temp = count[j];count[j] = pos;pos += temp;}count[0] = 0;//将所有桶中记录依次收集到tmp中for (j = 0; j < n; j++){k = (arr[j] / radix) % 10; //计算arr[j]的基数tmp[count[k]] = arr[j]; //将arr[j]放入基数为k的第一个可用位置count[k]++; //基数为k的第一个可用位置往后移一位}
通常的写法是如下这样的,大家可以对比一下这两种写法,选用自己更容易理解的实现方法。
![c79cc03737d6ef4a322d8758ef7e5daa.png](https://i-blog.csdnimg.cn/blog_migrate/213aa683cc85ced4a30eff81d8440e59.jpeg)
for (i = 1; i < 10; i++) count[i] += count[i - 1]; // 将数据存储到临时数组output[]中for (i = n - 1; i >= 0; i--){ k = (arr[j] / radix) % 10; //计算arr[j]的基数 tmp[count[k] - 1] = a[i]; count[k]--;}
![aa69f814d988f5e65ebef960f5b4c8c9.png](https://i-blog.csdnimg.cn/blog_migrate/f8445e435a1a6a474b7cb83e9f475e09.jpeg)
基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要m的时间复杂度,m为桶的个数。假如待排数据可以分为k个关键字,则基数排序的时间复杂度将是O(k*(n+m)) ,通常n>>m,因此基本上还是线性级别的。
基数排序的空间复杂度为O(n+m),其中m为桶的数量。通常n>>m,因此额外空间需要大概n个左右。
![fdfeda318d1328211c670520654d8dec.png](https://i-blog.csdnimg.cn/blog_migrate/e82087c2760d1984bb0c9b2882695dd1.jpeg)