【数据结构】排序算法系列——基数排序(附源码+图解)

基数排序

基数排序的发明可以追溯到1887年赫尔曼·何乐礼打孔卡片制表机上的贡献[1]。基数排序算法早在1923年被广泛运用在打孔卡的排序。

它是一种典型的非比较型排序算法,所含的算法思想同计数排序有所相似,同时它也不仅可以被用于整数,

算法思想

它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

它使用桶来对同一位数的不同大小进行分装,首先进行个位数的排序存放,再进行十位数的排序存放,然后是百位数...我们可以通过取出最大值来确定最高位数,从而确定需要进行几轮的排序存放操作。

基数排序的方式可以采用LSD(Least significant digital)MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

图解

代码分析

(注:本段摘自网络,因为觉得写得很好!)

c++ 复制代码
#include<iostream>
#include<stdlib.h>
using namespace std;
void Radixsort(int a[], int length)//基数排序
{
	int max = a[0], base = 1;//base与max用来判断进行几趟排序
	for (int i = 1; i < length; i++)
	{
		if (max < a[i]) max = a[i];
	}
	int* t = (int*)malloc(sizeof(int) * length);//用t数组临时储存每一趟排好的结果
	while (max / base > 0)//最大数为n位数就进行n次
	{
		int bucket[10] = { 0 };
		for (int i = 0; i < length; i++)//统计每个桶中的数目
			bucket[a[i] / base % 10]++;
		for (int i = 1; i < length; i++)//将各个桶中的数目相加
			bucket[i] += bucket[i - 1];
		for (int i = length - 1; i >= 0; i--)//将数据依次放入桶中
		{
			t[bucket[a[i] / base % 10] - 1] = a[i];
			bucket[a[i] / base % 10]--;
		}
		for (int i = 0; i < length; i++)//将排好的数据放回a数组中
			a[i] = t[i];
		base = base * 10;
	}
}

我们需要分析一下这段代码:

  • 第一个for
c++ 复制代码
for(int i=0;i<length;i++)
		   bucket[a[i]/base%10]++;

这里是算出每个桶内有几个数,bucket[i]代表i号桶内有bucket[i]个数。

  • 第二个for
c++ 复制代码
for(int i=1;i<10;i++)
		   bucket[i]+=bucket[i-1];
  • 第三个for
c++ 复制代码
for(int i=length-1;i>=0;i--)
		   {t[bucket[a[i]/base%10]-1]=a[i];
		   bucket[a[i]/base%10]--;} 

bucket[a[i]/base%10]就是看a[i]在哪个桶内;-1是观察到在x号桶内的数据要存入t数组的话在t数组的下标是bucket[x]-1;

另一个要注意的是这里是从最后一个数开始存,是因为同一个桶里如果有两个数字 ,那么下面的一个数字在原序列中一定排在上面那个数字的后面,不能够重合。收集的时候如果从前往后收集,就是先收集上面的数字,存放的位置下标不好计算,并不知道桶里有几个数字。

MSD 基数排序

基于 k - 关键字元素的比较方法,可以想到:先比较所有元素的第1关键字,就可以确定出各元素大致的大小关系;然后对 具有相同第1关键字的元素,再比较它们的第2关键字......以此类推。

由于是从第1关键字到第k关键字顺序进行比较,由上述思想导出的排序算法称为 MSD(Most Significant Digit first)基数排序。

LSD 基数排序

MSD 基数排序从第1关键字到第k关键字顺序进行比较,为此需要借助递归或迭代来实现,时间常数还是较大,而且在比较自然数上还是略显不便。

而将递归的操作反过来:从第k关键字到第1关键字顺序进行比较,就可以得到 LSD(Least Significant Digit first)基数排序,不使用递归就可以完成的排序算法。

时间复杂度

基数排序的时间复杂度可以看成是:O(k*n),n是排序元素的个数,k是位数。这个复杂度往往是优于O(nlogn)的;k的大小决定了要进行的轮数,n是每轮要处理的个数。

基数排序是否比基于比较的排序算法(如快速排序)更好呢?通常情况要比快速排序的期望运行时间代价更好一些。但是,在处理的n个关键字时,尽管基数排序执行的循环轮数会比快速排序要少,但每一轮它所耗费的时间要长得多 。哪一个排序算法更合适依赖于具体实现和底层硬件的特性 (例如,快速排序通常可以比基数排序更有效地使用硬件的缓存),以及输人数据的特征。此外,利用计数排序作为中间稳定排序的基数排序不是原址排序 ,而很多O(nlogn)时间的比较排序是原址排序。因此,当主存的容量比较宝贵时,我们可能会更倾向于像快速排序这样的原址排序算法。

稳定性

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

相关推荐
爱吃生蚝的于勒15 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~18 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
lc寒曦41 分钟前
【VBA实战】用Excel制作排序算法动画
排序算法·excel·vba
王哈哈^_^43 分钟前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt
星沁城1 小时前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
脉牛杂德1 小时前
多项式加法——C语言
数据结构·c++·算法
legend_jz1 小时前
STL--哈希
c++·算法·哈希算法
kingmax542120081 小时前
初三数学,最优解问题
算法
一直学习永不止步1 小时前
LeetCode题练习与总结:赎金信--383
java·数据结构·算法·leetcode·字符串·哈希表·计数
小刘|2 小时前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法