所有排序算法以升序为例。
非线性时间比较类排序
交换排序
交换排序类的排序算法的基本思想是通过不断交换两个元素的位置来进行排序。经典的有冒泡排序和快速排序两种。
冒泡排序
通过不断地对相邻两元素进行位置交换达成排序的目的。
原始序列:
[5, 3, 38, 15, 23, 26, 3, 2]
- 比较两个相邻元素,如果前一个比后一个大则交换它们。
[3, 5, 38, 15, 23, 26, 3, 2]
- 每一对相邻元素做同样的工作,直到序列末尾,此时序列中最大的元素已经交换到末尾。
[3, 5, 15, 23, 26, 3, 2, 38]
- 不带最后一个元素重复以上步骤。
最好情况时间复杂度:顺序,
最坏情况时间复杂度:全部逆序,
平均时间复杂度:
空间复杂度:不占用额外空间,
稳定性:稳定
快速排序
利用“分治”的思想。通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
原始序列:
[5, 2, 38, 15, 23, 26, 3, 3]
- 从待排序序列中选择一个“基准”(pivot)
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
[3, 2, 3, 5, 23, 26, 15, 38]
- 递归地把基准值前后的序列进行排序
[2, 3, 3, 5, 15, 23, 26, 38]
最好情况时间复杂度:每一次取到的元素都刚好平分整个序列,
最坏情况时间复杂度:每次取到的都是序列中的最大/最小元素,
平均时间复杂度:
,这有个知乎的帖子计算平均时间复杂度,最好还是参考《算法导论》。
空间复杂度:
稳定性:不稳定
选择排序
选择排序可以分为简单选择排序和堆排序。
简单选择排序
简单选择排序的原理是:首先在序列中找到最小元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小元素,放到已排序序列的末尾。
原始序列
[5, 5, 38, 15, 23, 26, 3, 2]
- 第一趟排序前,有序区为空,无序区为[1, n]
- 从无序区所有元素中选择最小元素,与无序区第1个元素交换位置
[2, 5, 38, 15, 23, 26, 3, 5]
- 不断重复上一步
最好情况时间复杂度:
最坏情况时间复杂度:
平均时间复杂度:
空间复杂度:
稳定性:不稳定
堆排序
堆排序的基本思想是:构建大顶堆来选出序列中最大的元素,将此元素放入序列的有序区,不断重复以上步骤。这有个详细的说明。
关于时间复杂度的计算。
最好情况时间复杂度:
最坏情况时间复杂度:
平均时间复杂度:
空间复杂度:
稳定性:不稳定,例如27, 27, 36, 3
这个序列
插入排序
插入排序可分为简单插入排序和希尔排序
简单插入排序
简单插入排序算法的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
最好情况时间复杂度:顺序,
最坏情况时间复杂度:完全逆序,
平均时间复杂度:
空间复杂度:
稳定性:稳定
希尔排序
希尔排序的基本思想是,先将整个待排序的记录序列分割成为若干子序列,然后分别对这些子序列进行直接插入排序。
原始序列:
[5, 5, 23, 2, 3, 26, 38, 15]
- 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
选择增量序列为[4, 2, 1] - 按增量序列个数k,对序列进行k趟排序;每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。
第一趟排序,t1=4
[3, 5, 23, 2, 5, 26, 38, 15]
第二趟排序,t2=2
[3, 2, 5, 5, 23, 15, 38, 26]
第三趟排序,t3=1
[2, 3, 5, 5, 15, 23, 26, 38]
最好情况时间复杂度:顺序的直接插入排序,
最坏情况时间复杂度:完全逆序的直接插入排序,
平均时间复杂度:通过大量实验得到,
空间复杂度:
稳定性:不稳定
归并排序
归并排序也利用了“分治”思想。即先使每个子序列有序,再对合并后的子序列排序。
二路归并排序
- 把长度为n的输入序列分成两个长度为n/2的子序列
- 借助一个临时数组,对这两个子序列分别采用归并排序
- 将两个排序好的子序列合并成一个最终的排序序列
最好情况时间复杂度:
,无论什么情况都需要log(n)趟,每趟o(n)
最坏情况时间复杂度:
平均时间复杂度:
空间复杂度:临时数组的大小,
稳定性:稳定
线性时间非比较类排序
不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,可以说是以空间换时间。
计数排序
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 计数排序要求输入的数据必须是有确定范围的整数。不同语言的实现不同。
- 找出待排序的数组中最大和最小的元素,建立长度为k的数组C
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 反向填充目标数组:从C[0]开始,将i存入新数组C[i]次。
最好情况时间复杂度:o(n+k)
最坏情况时间复杂度:o(n+k)
平均时间复杂度:o(n+k)
空间复杂度:O(n+k)
稳定性:稳定
桶排序
桶排序的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
- 申请一定数量的桶
- 按照一个设定好的映射函数,将序列里面的元素映射到对应的桶里
- 遍历一遍所有的桶,将各个桶内的元素进行排序
- 将所有桶内的元素按照顺序组合起来
时间复杂度:因每个桶使用的排序方法而异
空间复杂度:O(n+k)
稳定性:不稳定
基数排序
基数排序是在计数排序基础上做的改进,多用于对整数进行排序。在基数排序中,我们利用一张10*n的表作为辅助对数字序列进行排序。
时间复杂度:
,其中d为数字的最高位数,n为序列长度,r为“基数”
空间复杂度:
稳定性:稳定