排序算法 | 平均时间复杂度 | 稳定性 | 空间复杂度 |
---|---|---|---|
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | 1 | O ( 1 ) O(1) O(1) |
选择排序 | O ( n 2 ) O(n^2) O(n2) | 不稳定 | O ( 1 ) O(1) O(1) |
插入排序 | O ( n 2 ) O(n^2) O(n2) | 1 | O ( 1 ) O(1) O(1) |
希尔排序 | O ( n 1.5 ) O(n^{1.5}) O(n1.5) | 不稳定 | O ( 1 ) O(1) O(1) |
快速排序 | O ( N l o g N ) O(NlogN) O(NlogN) | 不稳定 | O ( l o g N ) O(log N) O(logN) |
归并排序 | O ( N l o g N ) O(NlogN) O(NlogN) | 1 | O ( N ) O(N) O(N) |
堆排序 | O ( N l o g N ) O(NlogN) O(NlogN) | 不稳定 | O ( 1 ) O(1) O(1) |
基数排序 | O ( d ( n + r ) ) O(d(n+r)) O(d(n+r)) | 是 | O ( 1 ) O(1) O(1) |
考虑排序用到的场景来选择合适的排序方式:
- 逐个添加元素,要保证每次的数组都是正序排序
- 直接给定完全乱序数组,需要排序:可能快排是一个好选择
- 给定有一定顺序的乱序数组:如果数据序列基本有序,使用插入排序会更加高效。
- 给定复杂度的排序:如果提到 l o g N logN logN一定和树结构/分治法相关,可以考虑快排、归并、堆排。
0 编程语言默认的排序方式 及 复杂度分析 及 底层使用的排序思想
Java中用Arrays类中的sort()
使用的是“经过调优的快速排序法”,可以对八大基本数据类型进行排序,默认是升序的;底层使用:算法
import java.util.Arrays;
int[] array= {
3,1,4,5,6,2,9,7,8};
Arrays.sort(array); // 静态方法
如果需要降序排序,需要借助比较器Comparator
,此时需要其对应的包装类【不如在倒着遍历一遍正序的】
1 快速排序【分治思想】
1.1 思路:
- 设置一个哨兵(从数组中随机选择一个元素都可,一般选择
A[0]
),设置两个指针i,j
,初始位置分别是两边界,将哨兵用一个 变量存储起来key = A[0]
。 - 先用
j
从后向前找比key
小的数,如果找到将其放到i
的位置;然后i
从前向后找比key
大的数,如果找到放到j
的位置;直到i==j
,一次交换结束,此时我们将哨兵key
放到了正确的位置,前面元素都小,后面元素都大。 - 然后分别对于哨兵哨兵划分出来的左右区间进行同样的操作,直到每一格小区间都只有一个数结束。
1.2 模拟过程:
0 1 2 3 4 5 6 7 8 9 (index)
72 6 57 88 60 42 83 73 48 85 (array)
--------------------NO 1---------------------
key = 72, i = 0, j = 9
(72) 6 57 88 60 42 83 73 48 85 (j=8时,48<72)
48 6 57 88 60 42 83 73 (48) 85 (i=3时,88>72)
48 6 57 (88) 60 42 83 73 88 85 (j=5时,42<72)
48 6 57 42 60 (42) 83 73 88 85 (i==j,寻找结束,已锁定key的真实位置)
[48 6 57 42 60] 72 [83 73 88 85]
分别对左右两个区间进行同样操作
[42 6] 48 [57 60] 72 [73] 83 [88 85]
[6] 42 48 57 [60] 72 73 83 [85] 88
6 42 48 57 60 72 73 83 85 88
1.3 时间复杂度:
- 最好: O ( N ∗ l o g N ) O(N*logN) O(N∗logN),每次选的哨兵归位后刚好平分整个数组
- 最坏: O ( N 2 ) O(N^2) O(N2