学习笔记|数据结构——桶排序、计数排序、基数排序
桶排序、基数排序、计数排序的时间复杂度是O(n),因此称之为线性排序
三种算法都是基于非比较的排序算法,都不涉及元素之间的比较操作,重点是掌握这些排序算法的使用场景
桶排序
核心思想:将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了
时间复杂度分析:如果要排序的数据有n个,把他们均匀的划分到m个桶内,每个桶里有k=n/m个元素。每个桶内部使用快速排序,时间复杂度位O(klogk)。m个桶排序的时间复杂度就是O(mklogk),因为k=n/m,所以整个桶排序的时间复杂度就是O(nlog(n/m)).当桶的个数接近于数据个数时,log(n/m)就是一个非常小的常量,这时候时间复杂度近似于O(n)。
在极端情况下,数据都被划分在一个桶里,那就退化为O(nlogn)的排序算法了
桶排序比较适合用在外部排序中,外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中
不涉及比较,如何排序的?
先找出待排序数组的最大值和最小值,根据桶容量,算出一共多少个桶,这样每个桶的范围都是已知的,遍历待排序数组,计算(arr[i]-minValue)/bucketSize
,就能得到放到哪个桶中,其中 bucket是一个二维数组,第一层存放桶的标记,第二个表示桶中元素的位置。bucket[bucketIndex][indexArr[bucketIndex]++],
其中indexArr[bucketIndex]存桶中元素个数,这样就将数据按大小分别放到对应桶中了
如果桶内数据太大,还可以继续划分
计数排序
计数排序是桶排序的一种特殊情况。当要排序的n个数据,所处的范围并不大的时候,比如最大值是K,就可以把数据划分成K个桶,每个桶内的数值都是相同的,省掉了桶内排序的时间
举例:高考排名,从0分到满分750分划分成751个桶,桶内都是分数相同的考生,所以不需要再进行排序。我们只需要扫描每个桶,将桶内的考生一次输出到一个数组中
只涉及扫描遍历操作,所以时间复杂度是O(n)
这些数据的有序性,是通过桶带进来的,因为桶天然带有有序性
计数排序的巧妙之处
1、找出序列中最大的数,确定桶数组的大小n = max+1
max=a[o];
for(int i =0;i<n;i++){
if(a[i]>max) max=a[i]
}
int[] c = new int[max+1];
2、创建桶数组,并初始化为0.
3、计算元素出现的次数,存入数组c中 c[a[i]]++
4、对桶数组顺序求和
for(int i =0;i<=max;i++){
c[i] = c[i] + c[i-1];
}
这时候c数组的元素,代表小于等于下标数值的元素有多少个
5、创建r储存排序结果 r[c[a[i]]-1] = a[i]; c[a[i]]--;
计数排序只能用在数据范围不大的场景中,如果数据范围K比要排序的数据n大很多,就不适合用计数排序了。计数排序只能给非负整数排序,如果要排序的数据是其他类型,要将其再不改变相对大小的情况下,转换为非负整数。如果考生成绩精确到小数后一位,就需要将所有的分数都先乘以10,转换成整数,然后再放到7510个桶中。如果要排序的数据中有负数,那就对每个数据加相同的数,转换成非负数再排序
基数排序
要排序的数据需要可以分隔出独立的位来比较,而且位之间有递进的关系,如果a数据的高位比b数据大,那剩下的低位就不用比较了。除此之外,每一位的数据范围不能太大,要可以用线性排序算法来排序,否则基数排序时间复杂度就无法做到O(n)了
将数据拆分成一位一位的,从低位开始,用计数排序算法进行排序,这个排序算法必须是稳定性算法,依次向高位递进,这样比较完最高位,排序就出来了。