排序算法总结
记录10个比较常用的排序:
- [冒泡排序](## 冒泡排序)
- [选择排序](## 选择排序)
- [插入排序](## 插入排序)
- [希尔排序](## 希尔排序)
- [归并排序](## 归并排序)
- [快速排序](## 快速排序)
- [堆排序(优先队列)](## 堆排序)
- [计数排序](## 计数排序)
- [桶排序](## 桶排序)
- [基数排序](## 基数排序)
- [总结](## 总结)
冒泡排序
最简单的排序之一,相邻元素两两比较,把小的数放到前面,过程类似水泡上升的过程,由此得名,每次冒泡能找到一个最小的数,总比较次数为$ \frac {n(n-1)}{2}$,总交换次数为$\frac{n(n-1)}{2}$算法时间复杂度为$O(n^2)$,每次排好序之后的值不会变,是稳定排序,代码:
1 | public class Bubble { |
选择排序
思想和冒泡类似,每次遍历后都会将最小的元素放到最前面,但是过程和冒泡排序不大一样,冒泡是与相邻的元素进行交换,而选择每次都闲确定最小值,然后再进行交换,总比较次数依然是$\frac{n(n-1)}{2}$,但是总交换次数为$n$,是不稳定排序,代码:
1 | public class SelectSort { |
插入排序
类似于扑克牌整理,左边都是有序的,每次从无序堆选一个插入到有序堆,当数组原来就有序的时候,是最好情况,只需要$n-1$次比较和$0$次交换,最坏情况是数组逆序,总共需要$\frac{n(n-1)}{2}$次比较和$\frac{n(n-1)}{2}$次交换,平均情况取中值,排序稳定,代码:
1 | public class InersrtSort { |
希尔排序
交换不相邻的元素对数组进行局部排序,然后使用插入排序将有序数组排序,算法复杂度$O(n) < O < O(n^2)$,平均算法复杂度大概是$O(n^{1.3})$,算法不稳定,代码:
1 | public class ShellSort { |
归并排序
主要思想是分治,将元素分解为各含有n/2个元素的子序列,用合并排序算法对两个子序列递归的进行排序,合并两个已经排好序的子序列得到结果。时间复杂度为$O(nlogn)$,不是原地排序,稳定,代码:
1 | public class MergeSort { |
快速排序
和归并排序一样,主要思想是分治,取一个flag:array[p]
将数组分为两部分左边都小于array[p]
右边都大于array[p]
,然后递归解决两个子数组,最好情况下和平均情况下时间复杂度为$O(nlogn)$,最坏时间复杂度为$O(n^2)$,原地排序,算法不稳定。代码:
1 | public class QuickSort { |
堆排序
基本思想是利用堆实现选择排序,不用每一次遍历整个待排数组空间,时间复杂度从$O(n^2)$降为$O(n^2)$,不稳定,原地排序,思想和代码如下:
1 | public class HeapSort { |
计数排序
时间复杂度为$o(n)$,前提条件为数据要是在一定范围内的整数,需要用很大的辅助空间,基本思想是,利用待排序数作为计数数组下标,统计每个数字的个数,然后依次输出,代码如下:
1 | import java.util.Arrays; |
桶排序
定制通,基于银蛇函数将序列映射到桶中,对桶中的数据进行排序,然后依次枚举输出桶中元素,可以看到,需要桶满足一定的条件,保证如果$k_1 < k_2$,那么$f(k_1) < f(k_2)$,分桶时间复杂度为$O(n)$,排序最好复杂度为$O(nlogn)$,对于N个数据,M个桶,平均时间复杂度为$O(N)+O(M\times\frac{N}{M}\times\log{\frac{N}{M}})$ = $O(N+N\times \log{N} - N \times \log{M})$,桶排序是稳定的,可以看到,当一个桶只有一个函数的时候,时间复杂度是线性的,空间复杂度相对而言就很高,代码如下:
1 | import java.util.ArrayList; |
基数排序
多次入桶,先按最小未大小排序,然后依次升高,最能能保证,在最高位的时候,桶中的数据是有序的,然后合并桶,算法稳定,代码如下:
1 | public class RadixSort { |
总结
名称 | 最好 | 最坏 | 平均 | 空间 | 稳定 | 方法 |
---|---|---|---|---|---|---|
冒泡排序 | $ O(n) $ | $O(n^2)$ | $O(n^2)$ | 1 | 稳定 | 交换 |
选择排序 | $O(n^2)$ | $O(n^2)$ | $O(n^2)$ | 1 | 稳定 | 选择 |
插入排序 | $ O(n) $ | $O(n^2)$ | $O(n^2)$ | 1 | 稳定 | 插入 |
希尔排序 | $ O(n\log n) $ | $ O(n^{\frac{4}{3}}) $ | 取决于差距序列 | 1 | 不稳定 | 插入 |
归并排序 | $ O(n\log n) $ | $ O(n\log n) $ | $ O(n\log n) $ | n | 稳定 | 归并 |
快速排序 | $ O(n\log n) $ | $ O(n^2) $ | $ O(n\log n) $ | 1 | 不稳定 | 分区 |
堆排序 | $ O(n) $ | $ O(n\log n) $ | $ O(n\log n) $ | 1 | 不稳定 | 选择 |
计数排序 | - | $ O(n+r) $ | $ O(n+r) $ | $ O(n+r) $ | 稳定 | 非比较 |
桶排序 | - | $ O(n+r) $ | $ O(n+r) $ | $ O(n+r) $ | 稳定 | 非比较 |
基数排序 | - | $ O(n\times \frac{k}{d}) $ | $ O(n\times \frac{k}{d}) $ | $ O(n+r) $ | 稳定 | 非比较 |
参考文献
[1] 维基
[2] 面试中常用的十大排序算法总结