线形排序
线形排序,就是时间复杂度为O(n)的排序算法,桶排序、计数排序、基数排序都是线形排序,之所以能做到线形排序的原因,是因为他们都是基于非比较的排序算法,都不涉及到元素之间的比较操作,线形排序的应用都是比较局限的,都是应用于特定的排序场景的。应用不是很广泛,但是如果数据的特征符合他们的特点,那么利用线形排序进行操作,会特别高效的。
2.桶排序
桶排序顾名思义就会用到痛,核心思想就是将要排序的数据分到几个有序的桶里。每个桶里边的数据再单独进行排序,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。
桶排序的时间复杂度
假设有n个数据分到了m个桶中,每个桶中的数据就是k=n/m个,每个桶中的数据通过快速排序的时间复杂度为O(klogk),那么m个桶的时间复杂度相加就为O(nlogk),k=n/m,当m趋紧与n的时候logk就为1,所以桶排序的时间复杂度就接近与O(n)。
桶排序的使用场景
既然桶排序的时间复杂度为O(n),比快速排序的效率还高,为什么不用桶排序代替其他的排序算法呢,实际上桶排序对数据的要求是非常苛刻的,应用十分局限。
1.桶排序要求数据能够很容易的划分出来m个桶,并且桶与桶之间有着大小顺讯,这样所有的铜排完,只需要按照顺序排序数据就可以了。
2.桶排序还需要每个桶里边的数据事均匀分布的,如果某个桶里边的数据出现特别多的情况,很不均匀,他的时间复杂度就不再是常量级别的了,极端的情况下还会退化到快速排序的时间复杂度。、
3.**桶排序适合用在外部排序中。**外部排序:数据存储在外部磁盘中,数据量特别大,内存有限,不够存储。
分析一个案例:
假设有5gb的订单,希望按照订单金额进行排序,但是内存很小,只有几百兆,没办法一次性的把这些订单的数据进行排序,怎么办?
利用桶排序的思想就可以解决,首先扫描一下所有订单的数据,找到订单金额的范围,假设范围在1到10万之间,可以把范围分成100个桶(文件),每个桶里存储1000个范围,从1开始,然后对每个区间里边的订单进行排序,
最后按照顺序合并这100个文件,的到的数据就是我们需要的数据了。
如果有到某个区间的数据特别多的情况下怎么办,对这个区间继续进行分区。放在n个桶中去。
2.计数排序
计数排序就是桶排序的一种特殊情况。就是排序的时候数据的范围是m,我们就直接分成m个桶,每个桶内的数据都是相同的,省掉了桶排序的时间。
那高考来举例。假设我们省的考生有一百万,怎么进行排序呢?
考生的分数范围都是在0-900之间的,我们就把考生的数据按照0-900分成901个桶,对应的分数就是0-900,每个桶里边放的就是对应分数的同学。然后再将数据放到一个数组中或者一个文件中,就实现了对考生分数的排序,他的时间复杂度也是O(n)。
计数排序为什么叫做计数排序呢?
计数排序的算法实现方式:
假设现在有十个考生,分数在0-6之间,分数记录在一个数组中A[n]={0,1,3,4,3,5,6,3,0,2}。
现在需要一个数组C(m)记录每个分数出现的次数C(m)={2,1,1,3,1,1,1},C(m)就代表桶。0 的位置放置的就是分数为0的同学,一次每个位置放置的就是各个分数的同学数量。
然后我们让S(m)={2,3,4,7,8,9,10},可以看出他存储的就是<=改分数学生的数量,然后从后向前扫描数组A[n],放到一次放到数组R[n]中。
例如第一个去到的数据是2,就取出S(m)索引为二的数据为4,也就是说2是数组中的第四个元素(放到R[n]中对应的索引就是3),然后把S(2)的值-1,继续进行下边的操作,取出完所有的数据后的到的数组就是一个有序数组了。从后往前去可以保证计数排序是一个稳定的排序算法。
3.基数排序
直接来说基数排序的应用案例:
对手机号进行排序,假设有十万个手机号,进行排序,怎么进行排序。
其实非常简单,快速排序也可以做到但是效率太低,桶排序计数排序,手机号的范围太大,也不适合,接下来就用到了基数排序了,就是先对手机号的最后一位进行排序,然后倒数第二位,知道手机号的最后一位,排序出来的数据就是一个有序的了。
为什么这样排序出来的数据就是有序的了,因为只要高位中一个手机号比另一个手机号大,那么其他的位数就不用看了。而且我们需要使用一个稳定的排序,使用不稳定的排序进行排序的话,会出现高位相同的情况下打乱原来排序好的数据的顺序,数据就不是有序的。
每一位的排序可以用桶排序或者计数排序,时间复杂度为O(n),要排序的数据为k位,那整体的时间复杂度就为
O(kn),因为k是有限的所以基数排序的时间复杂度就近似于O(n)。
实际上要排序的数据往往都不是等长的,这样我们就需要对数据进行处理,利用的其实就是除法的思想讲这些数据归位一类,进行处理。
堆排序
堆排序的时间复杂度和快速排序的时间复杂度一样,有兴趣的可以看下我的这篇博客堆排序
排序总结
-排序- | -时间复杂度- | -原地排序- | -稳定性- |
---|---|---|---|
冒泡排序 | O(n^2) | 是 | 稳定 |
插入排序 | O(n^2) | 是 | 稳定 |
选择排序 | O(n^2) | 是 | 不稳定 |
归并排序 | O(nlogn) | 不是 | 稳定 |
快速排序 | O(nlogn) | 是 | 不稳定 |
堆排序 | O(nlogn) | 否 | 不稳定 |
桶排序 | O(n) | 否 | 稳定 |
计数排序 | O(n) | 否 | 稳定 |
基数排序 | O(n) | 否 | 稳定 |
在实际开发中并不是时间复杂度越小越好比如线形排序,就需要非常特定的场景,而且数据量小的时候时间复杂度为O(n^2)的有可能比O(nlogn)更高,因为我们分析时间复杂度的时候往往都是去掉参数,和常量的。所以根据具体的场景选择具体的排序就是仁者见仁,智者见智的事情了,大家可以分析一下java的排序,他就是根据具体的数据量选择不同的排序算法的。