由于面临找工作的压力,最近一直在准备笔试的东西。由于排序经常会被笔到,所以自己从网上搜集并根据自己的理解加以整理。
主要参考资料为:经典排序算法集锦——http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html
维基百科排序算法——https://zh.wikipedia.org/wiki/排序
以下是自己整理的东西,里面有些笔者不太确定的,红色标记即是,实在不确定就空出来了。
描述 | 示例 | 时间复杂度 | 额外空间 | 稳定性 | 是否会退化 | 退化情形 | |
快速排序 | 通过一趟扫描,将要排序的数据以某个数据为轴,分为两个部分,一个部分中的数据都比轴数小,另一部分都比轴数,然后按此方法对两部分数据进行快速排序 | 无序数组[3 8 2 4 9 6 1] a) 先把第一项作为轴,和其他数据进行比较,排序后数组为[2 1 3 8 4 9 6] b) 然后对前半部分和后半部分分别进行同样的排序操作 | O(n log n) | O(1) | 不稳定 | 是 | 每次划分选取的基准是待排序区的最小或最大记录 |
桶排序 | 待排序数组中的数字同属于一个有限的区间,例如范围为10以内,则声明一个长度为10 的空桶,将待排序数组的数字对应到空桶的相应位置上,然后依次输出数组即可 | 数组[3 5 2 7 4 6] a)数组内的数字都限为10以内整数,可以声明长度为10的空桶。 空桶[0 0 0 0 0 0 0 0 0 0] b)按照 空桶[待排数组[i]]=待排数组[i]进行赋值 桶[0 0 2 3 4 5 6 7 0 0 ] c)依次输出桶中的数值 | O(n) | O(N) | 稳定 | 否 |
|
插入排序 | 类似于打扑克牌时码牌一样,每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。 | [5 3 6 7 1] [3 5 6 7 1] [3 5 6 7 1] [3 5 6 7 1] [1 3 5 6 7] | 平均O(n^2) 最优O(n) 最差O(n^2) | O(1) | 稳定 | 否 |
|
基数排序 | 类似桶排序,不过只需要10个桶,分多次入桶,第一次按照个位数字顺序入桶,第二次按照十位数字顺序入桶,依次类推 | 待排序数组[62,14,59,88,16]简单点五个数字 分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样 | 0 | 0 | 62 | 0 | 14 | 0 | 16 | 0 | 88 | 59 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号
将桶里的数字顺序取出来,输出结果:[62,14,16,88,59]
再次入桶,不过这次以十位数的数字为准,进入相应的桶,变成下边这样:
由于前边做了个位数的排序,所以当十位数相等时,个位数字是由小到大的顺序入桶的,就是说,入完桶还是有序
| 0 | 14,16 | 0 | 0 | 0 | 59 | 62 | 0 | 88 | 0 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号
因为没有大过100的数字,没有百位数,所以到这排序完毕,顺序取出即可
最后输出结果:[14,16,59,62,88] | O(k*n) | O(r+n) 复用同一组桶,r表示桶个数,n表示数组长度 | 稳定 | 否 |
|
鸽巢排序 | 类似桶排序,需要一个很大的鸽巢,也即数组,数组的索引位置就表示值,该索引位置的值表示出现次数。 | [2 2 5 5 7 3 1 1 1 4 4] 鸽巢[0 0 0 0 0 0 0 0 0 0] 将数值对应到鸽巢索引后: [0 3 2 1 2 2 0 1 0 0 ] 顺序输出:[1 1 1 2 2 3 4 4 5 5 7] | O(n) | O(N) | 稳定 | 否 |
|
归并排序 | 把原始数组分成若干子数组,对每个子数组进行排序,然后子数组间进行合并 | 无序数组[6 2 4 1 5 9] 先看一下每个步骤下的状态,完了再看合并细节 第一步 [6 2 4 1 5 9]原始状态 第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍 第三步 [1 2 4 6] [5 9]继续两组两组合并 第四步 [1 2 4 5 6 9]合并完毕,排序完毕 输出结果[1 2 4 5 6 9] | 平均O(nlogn) 最差O(nlogn) 最优O(n) | O(n) | 稳定 | 否 |
|
冒泡排序 | 临近数字进行比较,按照正序或逆序进行交换,进行多次遍历,知道无可交换的临近值 |
| O(n^2) 最优O(n) 最差O(n^2) | O(1) | 稳定 | 是 | 数组有序,但是是反序 |
选择排序 | 从待排序数组里选择最小(最大)的数字,每次都去一个最小数字出来,顺序放到新数组中 |
| O(n^2) | O(1) | 不稳定 | 否 |
|
鸡尾酒排序 | 冒泡排序的一种变形,排序时从低到高,然后从高到低。假如一趟来回没有交换任何数字,则表示数组已排好序。 |
| O(n^2) | O(1) | 稳定 | 否 |
|
希尔排序 | 递减增量排序算法,是插入排序的一种高速的改进版本。 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。 | 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组,就是每隔3个数取一个,每隔三个再取一个,这样取出来的数字放到一组,把它们当成一组,但不实际分组,只是当成一组来看,所以上边的"组"实际上并不存在,只是为了说明分组关系对以上三组分别进行插入排序变成下边这样 [1 6] [2 5] [4 9] 具体过程: [6 1]6和1交换变成[1 6] [2 5]2与5不动还是[2 5] [4 9]4与9不动还是[4 9] 第一趟排序状态演示: 待排数组:[6 2 4 1 5 9] 排后数组:[1 2 4 6 5 9] 第二趟关键字取的是1,即每隔一个取一个组成新数组,实际上就是只有一组啦,隔一取一就全部取出来了嘛 此时待排数组为:[1 2 4 6 5 9] 直接对它进行插入排序 | 最差O(n log^2 n) 最优 O(n) | O(n) | 不稳定 |
|
|
堆排序 | 堆是一个近似完全二叉树的结构,并同时满足:子节点的值总是小于(大于)它的父节点。
通常堆是通过一维数组来实现的。在起始数组为 0 的情形中: 父节点i的左子节点在位置 (2*i+1); 父节点i的右子节点在位置 (2*i+2); 子节点i的父节点在位置 floor((i-1)/2); | 在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作: 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点 创建最大堆(Build_Max_Heap):将堆所有数据重新排序 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算 | O(n log n) | O(n) | 不稳定 | 否 |
|
地精排序 | 号称最简单的排序算法,只有一层循环,默认情况下前进冒泡,一旦遇到冒泡的情况发生就往回冒,知道把这个数字放好为止。Gnome Sort | void gnomesort(int n, int ar[]) { int i = 0; while (i < n) { if (i == 0 || ar[i-1] <= ar[i]) i++; else { int tmp = ar[i]; ar[i] = ar[i-1]; ar[--i] = tmp;} } } | O(n^2) | O(1) | 稳定 | 是 | 数组有序切为反序 |
奇偶排序 | 比较性质的排序,基本思路是奇数列排一趟序,偶数列排一趟序,再技术列排序,再偶数列排序,循环往复,知道有序。 |
| O(n^2) | O(1) | 稳定 |
|
|
梳排序 | 梳排序是改良自冒泡排序和快速排序的排序方法。梳排序是对固定距离处的数比较和交换,类似希尔,这个固定距离是待排数组长度除以1.3得到近似值,下次则以上次得到的近似值再除以1.3,知道距离小至3时,以1递减。 | 假设待数组[8 4 3 7 6 5 2 1] 待排数组长度为8,而8÷1.3=6,则比较8和2,4和1,并做交换 [8 4 3 7 6 5 2 1] 交换后的结果为 [2 1 3 7 6 5 8 4] 第二次循环,更新间距为6÷1.3=4,比较2和6,1和5,3和8,7和4 [2 1 3 7 6 5 8 4] 只有7和4需要交换,交换后的结果为 [2 1 3 4 6 5 8 7] 第三次循环,更新距离为3,没有交换 第四次循环,更新距离为2,没有交换 第五次循环,更新距离为1,三处交换 [2 1 3 4 6 5 8 7] 三处交换后的结果为[1 2 3 4 5 6 7 8] 交换后排序结束,顺序输出即可得到[1 2 3 4 5 6 7 8] | O(n log n) | O(1) | 不稳定 |
|
|
耐心排序 | 这个排序的关键在建桶和入桶规则上 建桶规则:如果没有桶,新建一个桶;如果不符合入桶规则那么新建一个桶 入桶规则:只要比桶里最上边的数字小即可入桶,如果有多个桶可入,那么按照从左到右的顺序入桶即可 然后将各桶一次输出,运用插入排序即可。 | 示例http://www.cnblogs.com/kkun/archive/2011/11/23/2260291.html
| O(n^2) | O(n) | 稳定 |
|
|
珠排序 | 在珠排序中,一行表示一个数字。如果一行里有2颗珠子,该行代表数字2;如果一行里有4颗珠子,该行代表数字4。给定一个数组,数组里有多少个数字,就要有多少行;数组里最大的数字是几,就要准备多少根杆子。 准备就绪后,释放珠子,珠子(按重力)下落,就完成了排序。 |
| O(n) | O(n^2) | 不稳定 |
|
|
计数排序 | 数组a,计数数组c,对于c[i]=a中比a[i]值小的值个数,最后数组d[c[i]]=a[i] | 此计数排序的定义和网上部分的计数排序定义不同 | O(n^2) | O(n) |
|
|
|
Proxmap Sort | 基于桶排序和基数排序,将桶划分成若干个桶,每个桶存放一个数值区间内的数字,相当于给数字分区 |
|
|
|
|
| |
Strand Sort | 每次遍历出待排序列中的有序子序列,取出和已取出的序列进行归并 | 待排数组[ 6 2 4 1 5 9 ] 第一趟遍历得到"子有序数组"[ 6 9],并将其归并排序到有序数组里 待排数组[ 2 4 1 5] 有序数组[ 6 9 ] 第二趟遍历再次得到"子有序数组"[2 4 5],将其归并排序到有序数组里 待排数组[ 1 ] 有序数组[ 2 4 5 6 9 ] 第三趟遍历再次得到"子有序数组"[ 1 ],将其归并排序到有序数组里 待排数组[ ... ] 有序数组[ 1 2 4 5 6 9 ] | O(n^2) | O(n) | 稳定 |
|
|
*稳定性:假定在排序序列中存在多个相同值的记录,经过排序后,这些记录的相对次序依然保持不变,则称之为稳定排序。
再附上两张维基算法的截图,以供对比参考
部分算法对比图
算法分类图