排序算法
| 算法 | 稳定性 | 时间复杂度 | 空间复杂度 | 备注 |
|---|---|---|---|---|
| 选择排序 | × | N^2 | 1 | |
| 冒泡排序 | √ | N^2 | 1 | |
| 插入排序 | √ | N ~ N^2 | 1 | 时间复杂度和初始顺序有关 |
| 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | 改进版插入排序 |
| 快速排序 | × | NlogN | logN | |
| 三向切分快速排序 | × | N ~ NlogN | logN | 适用于有大量重复主键 |
| 归并排序 | √ | NlogN | N | |
| 堆排序 | × | NlogN | 1 | 无法利用局部性原理 |
快速排序
快排的最坏时间复杂度,选的初始值是最大值或者是最小值,这样就是N-1+N-2+….1=O(n^2).
最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况,就是二叉树的层数
最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况
1 | /* |
改进版的快速排序,分为<num等于num以及大于num三个子区间,这样的话当数据中有大量相同的数据,就可以大大节省时间。
1 | public static void quick_sort2(int[] nums,int left,int right){ |
75.荷兰国旗问题
1 | /** |
归并排序
合二为一。将两个排序好的数组进行合并,使用双指针。
空间复杂度:O(N),即tmp数组
时间复杂度:O(nlogN),可有公式求出
1 | /** |
小和问题
主要借助归并排序来解决。类似于可以划分为左右子区间的问题都可以使用递归方式来解决。

1 | public static int merge(int[] arr,int left ,int right){ |
冒泡排序
时间复杂度为O(n^2)。n-1+n-2+….2=n^2/2
从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。
每一次循环都会有一个位置的元素确定。
1 | public static void bubbleSort(int[] arr){ |
选择排序
与冒泡排序差不多,与冒泡排序最大的区别就就是它的交换元素每一次只交换一次,选择排序是不稳定的。
1 | public static void selectSort(int[] arr){ |
插入排序
时间复杂度最差是O(n^2),逆序的情况下,最好是O(n),顺序的情况下。
1 | public void insertSort(int[] arr){ |
堆排序
堆结构
堆的插入删除时间复杂度是logN,即树的高度,因此,效率很高。
堆实际上是一个完全二叉树,堆可以用数组来表示,位置 k 的节点的父节点位置为 (k-1)/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。
大顶堆:父节点的值大于他所有子孙的值。
小顶堆:父节点的值小于他所有子孙的值。
1 | //上浮操作 |
非基于比较器的排序
桶排序,基数排序,计数排序。

采用桶的思想来解决。
1 | /** |
递归程序时间复杂度计算

例如:
1 | /** |
该递归程序每次需要递归两次子区间,则a = 2,每个递归子区间大小是N/2,因此b = 2,而每次执行完还需要做O(1)的操作,因此,d= 0,log(b,a)=1>d,所以复杂度为O(N^log(2,2))=O(N)
工程中的排序算法
一般来说,是综合多种排序算法的。
假如数据量很小,使用插入排序;样本量很大,数据类型是基础类型,使用快排,数据类型是类,使用归并排序,可以保证数据稳定性。