当前位置: 首页 > news >正文

【数据结构】排序算法---基数排序

在这里插入图片描述

文章目录

  • 1. 定义
  • 2. 算法步骤
    • 2.1 MSD基数排序
    • 2.2 LSD基数排序
  • 3. LSD 基数排序动图演示
  • 4. 性质
  • 5. 算法分析
  • 6. 代码实现
    • C语言
    • Python
    • Java
    • C++
    • Go
  • 结语

⚠本节要介绍的不是计数排序

1. 定义

基数排序(英语:Radix sort)是一种非比较型的排序算法,最早用于解决卡片排序的问题。基数排序将待排序的元素拆分为k个关键字,逐一对各个关键字排序后完成对所有元素的排序。

  • 如果是从第1关键字到第k关键字顺序进行比较,则该基数排序称为 MSD(Most Significant Digit first)基数排序——最高位优先法(从高位到低位);

  • 如果是从第k关键字到第1关键字顺序进行比较,则该基数排序称为 LSD(Least Significant Digit first)基数排序——最低位优先法(从低位到高位)。

基数排序是将整数按位数切割成不同的数字,然后按每个位数分别比较,由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

2. 算法步骤

2.1 MSD基数排序

将待排序的元素拆分为k个关键字,先对第1关键字进行稳定排序,然后对于每组 具有相同关键字的元素 再对第2关键字进行稳定排序(递归执行)……最后对于每组 具有相同关键字的元素 再对第k关键字进行稳定排序。

在这里插入图片描述

一般而言,我们默认基数排序是稳定的,所以在 MSD 基数排序中,我们也仅仅考虑借助 稳定算法(通常使用计数排序)完成内层对关键字的排序。

2.2 LSD基数排序

将待排序的元素拆分为k个关键字,然后先对 所有元素 的第k关键字进行稳定排序,再对 所有元素 的第k-1关键字进行稳定排序,再对 所有元素 的第k-2关键字进行稳定排序……最后对 所有元素 的第1关键字进行稳定排序,这样就完成了对整个待排序序列的稳定排序。

在这里插入图片描述

LSD 基数排序也需要借助一种 稳定算法 完成内层对关键字的排序。同样的,通常使用计数排序来完成。

3. LSD 基数排序动图演示

在这里插入图片描述

4. 性质

稳定性

如果对内层关键字的排序是稳定的,则 MSD 基数排序和 LSD 基数排序都是稳定的排序算法。

空间复杂度

MSD 基数排序和 LSD 基数排序的空间复杂度都为 O ( k + n ) O(k+n) O(k+n)

时间复杂度

基数排序每一位的比较可以使用线性排序,比如桶排序或者计数排序,当然需要保证如计数排序的稳定性。每次排序时间复杂度O(n),那么如果有k位,则时间复杂度为 O ( k ∗ n ) O(k*n) O(kn),如果k不大比如手机号11位,那么时间复杂度就是 O ( n ) O(n) O(n)

通常而言,基数排序比基于比较的排序算法(比如快速排序)要快。但由于需要额外的内存空间,因此当内存空间稀缺时,原地置换算法(比如快速排序)或许是个更好的选择。

一般来说,如果每个关键字的值域都不大,就可以使用 [计数排序]作为内层排序,此时的复杂度为 O ( k n + ∑ i = 1 k w i ) O(kn+{\sum_{i=1}^k}wi) O(kn+i=1kwi),其中 w i wi wi为第i关键字的值域大小。如果关键字值域很大,就可以直接使用基于比较的 O ( n k l o g n ) O(nklogn) O(nklogn)排序而无需使用基数排序了。

5. 算法分析

基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要 O ( n ) O(n) O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要 O ( n ) O(n) O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是 O ( d ∗ 2 n ) O(d*2n) O(d2n),当然d要远远小于n,因此基本上还是线性级别的。

基数排序的空间复杂度为O(n+k),其中k为桶的数量。一般来说 n > > k n>>k n>>k,因此额外空间需要大概n个左右。

6. 代码实现

C语言

#include<stdio.h>
#define MAX 20
//#define SHOWPASS
#define BASE 10void print(int *a, int n) {int i;for (i = 0; i < n; i++) {printf("%d\t", a[i]);}
}void radixsort(int *a, int n) {int i, b[MAX], m = a[0], exp = 1;for (i = 1; i < n; i++) {if (a[i] > m) {m = a[i];}}while (m / exp > 0) {int bucket[BASE] = { 0 };for (i = 0; i < n; i++) {bucket[(a[i] / exp) % BASE]++;}for (i = 1; i < BASE; i++) {bucket[i] += bucket[i - 1];}for (i = n - 1; i >= 0; i--) {b[--bucket[(a[i] / exp) % BASE]] = a[i];}for (i = 0; i < n; i++) {a[i] = b[i];}exp *= BASE;#ifdef SHOWPASSprintf("\nPASS   : ");print(a, n);
#endif}
}int main() {int arr[MAX];int i, n;printf("Enter total elements (n <= %d) : ", MAX);scanf("%d", &n);n = n < MAX ? n : MAX;printf("Enter %d Elements : ", n);for (i = 0; i < n; i++) {scanf("%d", &arr[i]);}printf("\nARRAY  : ");print(&arr[0], n);radixsort(&arr[0], n);printf("\nSORTED : ");print(&arr[0], n);printf("\n");return 0;
}

Python

# coding=utf-8
def radix_sort(array):max_num = max(array)place = 1while max_num >= 10**place:place += 1for i in range(place):buckets = [[] for _ in range(10)]for num in array:radix = int(num/(10**i) % 10)buckets[radix].append(num)j = 0for k in range(10):for num in buckets[k]:array[j] = numj += 1return arrayif __name__ == '__main__':array = [25, 17, 33, 17, 22, 13, 32, 15, 9, 25, 27, 18]print(radix_sort(array))

Java

/*** 基数排序* 考虑负数的情况还可以参考: https://code.i-harness.com/zh-CN/q/e98fa9*/
public class RadixSort implements IArraySort {@Overridepublic int[] sort(int[] sourceArray) throws Exception {// 对 arr 进行拷贝,不改变参数内容int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);int maxDigit = getMaxDigit(arr);return radixSort(arr, maxDigit);}/*** 获取最高位数*/private int getMaxDigit(int[] arr) {int maxValue = getMaxValue(arr);return getNumLenght(maxValue);}private int getMaxValue(int[] arr) {int maxValue = arr[0];for (int value : arr) {if (maxValue < value) {maxValue = value;}}return maxValue;}protected int getNumLenght(long num) {if (num == 0) {return 1;}int lenght = 0;for (long temp = num; temp != 0; temp /= 10) {lenght++;}return lenght;}private int[] radixSort(int[] arr, int maxDigit) {int mod = 10;int dev = 1;for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {// 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)int[][] counter = new int[mod * 2][0];for (int j = 0; j < arr.length; j++) {int bucket = ((arr[j] % mod) / dev) + mod;counter[bucket] = arrayAppend(counter[bucket], arr[j]);}int pos = 0;for (int[] bucket : counter) {for (int value : bucket) {arr[pos++] = value;}}}return arr;}/*** 自动扩容,并保存数据** @param arr* @param value*/private int[] arrayAppend(int[] arr, int value) {arr = Arrays.copyOf(arr, arr.length + 1);arr[arr.length - 1] = value;return arr;}
}

C++

int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{int maxData = data[0];              ///< 最大数/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。for (int i = 1; i < n; ++i){if (maxData < data[i])maxData = data[i];}int d = 1;int p = 10;while (maxData >= p){//p *= 10; // Maybe overflowmaxData /= 10;++d;}return d;
/*    int d = 1; //保存最大的位数int p = 10;for(int i = 0; i < n; ++i){while(data[i] >= p){p *= 10;++d;}}return d;*/
}
void radixsort(int data[], int n) //基数排序
{int d = maxbit(data, n);int *tmp = new int[n];int *count = new int[10]; //计数器int i, j, k;int radix = 1;for(i = 1; i <= d; i++) //进行d次排序{for(j = 0; j < 10; j++)count[j] = 0; //每次分配前清空计数器for(j = 0; j < n; j++){k = (data[j] / radix) % 10; //统计每个桶中的记录数count[k]++;}for(j = 1; j < 10; j++)count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中{k = (data[j] / radix) % 10;tmp[count[k] - 1] = data[j];count[k]--;}for(j = 0; j < n; j++) //将临时数组的内容复制到data中data[j] = tmp[j];radix = radix * 10;}delete []tmp;delete []count;
}

Go

var (K int = 3 // 基数排序需要的全局变量RADIX int = 10queue [][]int
)
func radix_sort_queue_pop(qu []int) []int {if len(qu) == 0 {return qu // 如果数组为空,不做任何操作}// 删除第一个元素qu = qu[1:]return qu
}
func radix_sort_queue_push(qu []int, data int) []int {qu = append(qu, data)return qu
}
func radix_sort_get_key(value int, k int) int {key := 0for k >= 0 {key = value % 10value /= 10k--}return key
}
func radix_sort_distribute(arr []int, left int, right int, k int){// k表示是第几次分发数据for i:=left; i<right; i++ {key := radix_sort_get_key(arr[i], k)queue[key] = radix_sort_queue_push(queue[key], arr[i])}
}
func radix_sort_collect(arr []int) {k := 0for i:=0; i < RADIX; i++ {for len(queue[i]) != 0 {arr[k] = queue[i][0] // 先进先出k++queue[i] = radix_sort_queue_pop(queue[i])}}
}
func radix_sort_by_group(arr []int, left int, right int) {for i:=0; i<K; i++ {// 分发数据radix_sort_distribute(arr, left, right, i)// 回收数据radix_sort_collect(arr)}
}
func radix_sort(arr []int, sz int) {// 初始化队列queue = make([][]int, RADIX)left := 0right := sz;radix_sort_by_group(arr, left, right)
}

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

带你初步了解排序算法:https://blog.csdn.net/2301_80191662/article/details/142211265
直接插入排序:https://blog.csdn.net/2301_80191662/article/details/142300973
希尔排序:https://blog.csdn.net/2301_80191662/article/details/142302553
直接选择排序:https://blog.csdn.net/2301_80191662/article/details/142312028
堆排序:https://blog.csdn.net/2301_80191662/article/details/142312338
冒泡排序:https://blog.csdn.net/2301_80191662/article/details/142324131
快速排序:https://blog.csdn.net/2301_80191662/article/details/142324307
归并排序:https://blog.csdn.net/2301_80191662/article/details/142350640
计数排序:https://blog.csdn.net/2301_80191662/article/details/142350741
桶排序:https://blog.csdn.net/2301_80191662/article/details/142375338
基数排序:https://blog.csdn.net/2301_80191662/article/details/142375592
十大经典排序算法总结与分析:https://blog.csdn.net/2301_80191662/article/details/142211564

在这里插入图片描述


http://www.mrgr.cn/news/30809.html

相关文章:

  • 《基于深度学习的车辆行驶三维环境双目感知方法研究》
  • windows中docker安装redis和redisinsight记录
  • 博弈论(零和博弈)英文版题解
  • 自动驾驶安全方向论文阅读
  • 实验室集群服务器常用命令-Linux
  • 显示器接口种类 | 附图片
  • 玩机进阶教程-----MTK芯片机型 回读 备份 导出分区来制作线刷包 其中MT****_Android_scatter.txt的修改 分区的写入与否
  • [杨雨贤发射器]python烟花代码
  • 电磁场与电磁波-1.3方向导数与梯度 1.4通量与散度
  • DEPLOT: One-shot visual language reasoning by plot-to-table translation论文阅读
  • Qt 常用数据类型
  • 在Spring项目中,两个实用的工具(生成类与映射文件、API自动生成)
  • 干耳屎硬掏不出来怎么办?质量最好的可视挖耳勺推荐
  • 2024自学网络安全的三个必经阶段(含路线图)
  • linux 进程间通信之pthread(条件变量共享和互斥锁共享)
  • 【吊打面试官系列-MySQL面试题】LIKE 声明中的%和_是什么意思?
  • 大模型研发全揭秘:如何通过评估指标优化大模型的表现?
  • C++中模板的初级使用函数模板(刚刚接触模板概念的小白也能明白)
  • MySQL篇(索引)(持续更新迭代)
  • Android 将EasyPermissions进一步封装,使得动态权限申请更加简明
  • 【devops】rsync介绍和使用
  • 利用echarts 显示图片信息
  • VMware虚拟机密码忘记了怎么办
  • kafka3.8的基本操作
  • 【Spring】IocDI详解(6)
  • mysql中的json查询