八大排序算法[三]

146 阅读4分钟

这是我参与更文挑战的第 9 天,活动详情查看: 更文挑战

排序

冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序

image-20210106105520078.png

排序算法的执行效率

  1. 最好情况,最坏情况,平均情况时间复杂度

    还要说出最好、最坏时间复杂度对应的要排序的原始数据是什么样的。

  2. 时间复杂度的系数,常数,低阶

  3. 比较次数和交换(或移动)次数

    基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,如果我们在分析排序算法的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去。

排序算法的内存消耗 算法的内存消耗可以通过空间复杂度来衡量,原地排序(Sorted in place),原地排序算法,就是特指空间复杂度是 O(1){O(1)} 的排序算法。

排序算法的稳定性

稳定的排序算法和不稳定的排序算法

冒泡排序是原地排序算法,空间复杂度为 O(1){O(1)},冒泡排序是稳定的排序算法。冒泡排序的最好情况时间复杂度是 O(n){O(n)},就是要排序的数据已经是有序的了,只需要进行一次冒泡操作。最坏的情况是,要排序的数据刚好是倒序排列的,需要进行 n 次冒泡操作,所以最坏情况时间复杂度为 O(n2){O(n^2)}。平均情况时间复杂度为 O(n2){O(n^2)}

插入排序是原地排序算法,空间复杂度为 O(1){O(1)},插入排序是稳定的排序算法。核心思想是取未排序区间的元素,在已排序区间找到合适的位置将其插入,并保证已排序区间数据一直有序。初始已排序区间只有一个元素,就是数组的第一个元素。 插入排序的最好时间复杂度为 O(n){O(n)},注意这里是从尾到头遍历已经有序的数据。如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以最坏情况时间复杂度为 O(n2){O(n^2)}。平均时间复杂度为 O(n2){O(n^2)}

选择排序是一种原地排序算法,空间复杂度为 O(1){O(1)},是一种不稳定的原地排序算法。算法的思想有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次都会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。选择排序最好,最坏,平均情况时间复杂度都为 O(n2){O(n^2)}

虽然冒泡排序和插入排序在时间复杂度都是一样的,都是 O(n2){O(n^2)},冒泡排序的数据交换要比插入排序的数据移动要复杂,但是如果我们希望把性能优化做到极致,那肯定选插入排序(优化:希尔排序)。

归并排序不是原地排序算法(致命弱点),这是因为归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间。它是一个稳定的排序算法,最好,最坏,平均情况时间复杂度都是 O(nlogn){O(nlogn)},空间复杂度是 O(n){O(n)}

快速排序是原地排序算法,不是一个稳定的排序算法。最好时间复杂度是 O(n){O(n)},是在分区及其均衡的情况下;最坏时间复杂度是 O(n2){O(n^2)},是在分区极不均衡的情况下;平均时间复杂度是 O(nlogn){O(nlogn)}

桶排序(Bucket sort),比较合适用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。

**计数排序(Counting sort)**其实是桶排序的一种特殊情况。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k,我们就可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。(900分和50万考生,时间复杂度是O(n){O(n)})。计数排序的时间复杂度都是O(n){O(n)}

计数排序只能用在数据范围不大的场景中,如果数据范围 k 比要排序的数据 n 大很多,就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,要将其在不改变相对大小的情况下,转化为非负整数。

基数排序(Radix sort)