前言:
排序有内部排序和外部排序,内部排序是在内存中进行的,外部排序是因为数据量过大,在内存中不能一次全部容纳。故在排序过程中访问硬盘等外存进行的。
在这,我主要说内部排序:
常用的内部排序算法有八种:
插入:
希尔排序VS直接插入排序
直接插入排序:
一句话总结:拿牌整牌(排排队)
这有一堆牌,从顶部拿一张,在手中按顺序排列。
每抽一张牌,和手里的牌(已排序的)进行比较,找到合适位置插入,再拿一张再比较.....直至所有的牌拿完。
如上图,当插入元素与已排序中元素相等时,新元素插入在已排序元素的后面,即插入排序是稳定的。
希尔排序:
一句话总结:分组,插入排序,分组比
Shell(希尔)排序也称为缩小增量排序。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
如n个数,则可取增量为d=n/2;
所有距离为d的数放在同一个组中:如d=5,第1个和第6个数间距离为5,则第1和第6在一组。第2个和第7个之间距离为5,则第2个和第7个为一组.......
先在各组内进行直接插入排序:如上图增量为5,5组分别进行直接插入排序。如上图第二行数;
然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止:如上图最后一轮,增量为1。则整个分成1组,这1组进行直接插入排序。
对比:
希尔排序是改进版的直接插入排序,
希尔排序开始由于增量d取值大,每个子序列元素少,排序速度快,待排序后期,增量值逐渐变小,子序列元素变多,但由于前面的工作基础,大多数元素已经有序,所以排序速度快。比直接插入排序效率高。
直接插入排序是稳定的,希尔排序是不稳定的。
选择:
简单选择排序VS堆排序
简单选择排序:
一句话总结:右上直角倒三角
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
如上图:结果为:13、27、38、[49]、49、65、76、97
但因为[49]本在49之后,但排完序后[49]在49的前面了,所以选择排序是不稳定的。
堆排序:
一句话总结:建堆取数重构堆
堆:具有n个元素的序列(k1,k2,...,kn),当且仅当满足
n个元素,初始化时,把要排序的n个数的序列看做一棵树,通过调整存储顺序,建立堆(小/大顶堆)。然后输出顶元素,将最后一个结点放在顶端空缺的位置,重构堆,重复上述操作,直至所有数取出。
如6个数:84、79、46、38、40、56
树:
建堆:
》》
输出:84
重构堆:
》》
》》
再输出79;
......
继而输出:84、79、56、46、40、38
对比:
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
两者都是找到最小/大值,输出,再找再输出。但在查找过程中,选择排序是在所有剩余元素中查找。堆排序是在顶元素的左/右子树或更深一层左/右子树的左/右子树中找,所以效率会高一些。
选择排序和堆排序都是不稳定的。
交换:
冒泡排序VS快速排序
冒泡排序:
一句话总结:左上直角倒三角
在一列要排序的元素中,从顶端第一个开始,依次向下进行比较,若发现小数在下,则进行交换,大数继续向下比较,即:大数下沉,小数上浮。
如第一列:49与38比较,49大,下沉,38上浮。
然后38与65比较,不变
......
最后,最大的97在最低端。
快速排序:
一句话总结:基准两边排
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
如:
全过程:
对比:
快速排序是冒泡排序的改进。快速排序,首先定一个基准,小的上浮,大的下沉。分组后,在各组中再次进行相同的操作。
冒泡排序是稳定的,快速排序是不稳定的。
归并排序:
一句话总结:二路归并
归并排序就是使用合并操作完成排序的算法。
归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
基数排序:
一句话总结:个十百千万
基数排序,是先按个位排序,排好序后,再按十位排,依次按更高位排。
如:
总结:
选择排序算法准则:
每种排序算法都各有优缺点。因此,在实用时需根据不同情况适当选用,甚至可以将多种方法结合起来使用。选择排序算法的依据
影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点:
1.待排序的记录数目n的大小;2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小;
3.关键字的结构及其分布情况;
4.对排序稳定性的要求。
就时间复杂度而言:
当原表有序或基本有序时,直接插入排序和冒泡排序将大大减少比较次数和移动记录的次数,时间复杂度可降至O(n);
而快速排序则相反,当原表基本有序时,将蜕化为冒泡排序,时间复杂度提高为O(n2);
原表是否有序,对简单选择排序、堆排序、归并排序和基数排序的时间复杂度影响不大。
就元素多少而言:
设待排序元素的个数为n.
1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序 :如果内存空间允许且要求稳定性的;
归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高;
2)当n较大,内存空间允许,且要求稳定性,则选择归并排序;
3)当n较小,可采用直接插入或直接选择排序。
直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。
直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序;
5)一般不使用或不直接使用传统的冒泡排序。
6)基数排序
它是一种稳定的排序算法,但有一定的局限性:
1、关键字可分解。
2、记录的关键字位数较少,如果密集更好
3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。