排序算法总结
总览
- 稳定:排序前后相等元素之间的相对位置不变
- 不稳定:排序前后相等元素的相对位置发生变化
- 内排序:所有排序操作在内存中完成
- 外排序:排序方法通过磁盘和内存的数据传输对大数据进行排序

- 比较排序:在排序的最终结果里,元素之间的次序依赖于它们之间的比较,每个数必须与其他数进行比较,才能确定自己的位置,其优势在于,适用于各种规模的数据,不在乎数据的分布。比较排序适用于一切需要排序的情况。
常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序。
- 非比较排序:只确定每个元素之前的已有元素个数即可进行排序,一次遍历即可解决,非比较排序时间复杂度低,但由于非比较排序需要占用空间来确定唯一位置,所以对数据规模和数据分布有一定的要求。
基数排序、计数排序、桶排序属于非比较排序。
一、冒泡排序
简单的排序算法,通过重复遍历需要排序的数组,比较相邻元素的大小并交换位置,一次遍历使一个元素归位到数组末端。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(1)
时间复杂度O(n^2^)
二、选择排序
表现最稳定的排序算法之一,总是O(n^2^)的时间复杂度,一般在数据规模较小的时候使用。其首先在未排序的序列中找到最小元素,将其放在序列起始位置,再从剩余未排序元素中继续寻找最小元素,放到已排序序列队尾,以此类推,直至排序完毕。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(1)
时间复杂度O(n^2^)
三、插入排序
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(1)
时间复杂度O(n^2^)
四、快速排序(重要)
通过一次排序将待排数据分隔为两部分,一部分数据均比另一部分数据小,再分别在两部分数据中继续排序,最后达到整体有序。
1、算法先从序列中挑出一个元素作为基准,然后将序列中所有比其小的元素放在左边,大的放在右边,即确定该基准在序列中的位置。
2、再递归地把小于基准值元素的子序列和大于基准值元素的子序列排序。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(logn)
时间复杂度O(nlogn)
五、归并排序(重要)
建立在归并操作上的一种有效的排序算法,是分治法的一个典型应用。
1、算法先将长度为n的序列分为两个n/2的子序列,再对子序列分别进行归并排序。
2、然后将两个排序好的子序列合并为最终的有序序列。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(n)
时间复杂度O(nlogn)
六、堆排序(重要)
利用堆的数据结构所设计的一种排序算法,堆是一个近似完全二叉树的结构,子节点的键值或索引总是小于(或大于)其父节点。
将堆的逻辑结构映射到数组中,则有以下性质:
对于大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
对于小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]
1、 将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素。
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆。
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(1)
时间复杂度O(nlogn)
七、希尔排序
希尔排序是一种插入排序,是简单插入排序的改进,也称为缩小增量排序。算法将数据按一定增量分组,对每组使用直接插入排序算法排序,随着增量逐渐减少,每组包含的数据越来越多,当增量减至1时,排序完毕。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(1)
时间复杂度O(nlog^2^n)~O(n^1.3–2^)
八、计数排序
计数排序使用一个额外的数组tmp,其中第i个元素时待排序数组arr中值等于i的元素的个数,然后根据数组tmp来将arr中的元素排到正确的位置。
计数排序只能对整数进行排序。
1、找出待排序数组中最大和最小的元素。
2、统计数组中每个元素i出现的次数,存入数组tmp的第i项。
3、对所有的计数累加,反向填充目标数组。

代码
1 | package sortalgorithms; |
空间复杂度O(n)
时间复杂度O(n)
九、基数排序
非比较排序算法,基数排序按照低位先排序,然后收集;再按照高位排序,收集;以此类推,直至最高位。
基数排序的两种方式:
高位优先,又称为最有效键(MSD),它的比较方向是由右至左;
低位优先,又称为最无效键(LSD),它的比较方向是由左至右;
基数排序只能对自然数进行排序。
1、取得数组中的最大数,并取得位数。
2、arr为原始数组,从最低位开始取每个位组成radix数组。
3、对radix进行计数排序


代码
1 | package sortalgorithms; |
分析
空间复杂度O(n)
时间复杂度O(n)
十、桶排序
桶排序就是把最大值和最小值之间的数进行瓜分,例如分成 10 个区间,10个区间对应10个桶,我们把各元素放到对应区间的桶中去,再对每个桶中的数进行排序,可以采用归并排序,也可以采用快速排序之类的。
之后每个桶里面的数据就是有序的了,我们在进行合并汇总。

代码
1 | package sortalgorithms; |
分析
空间复杂度O(n)
时间复杂度O(n)