校招准备系列5-排序算法

常用排序算法总结
来源:SteveWang

www.cnblogs.com/eniac12/p/5329396.html#s32
具体代码实现请看原博主的博客

内部排序算法,即数据记录在内存中进行排序。

排序算法大体可分为两种:

  • 一种是比较排序,时间复杂度O(nlogn) ~
    O(n^2),主要有:冒泡排序,选择排序,插入排序,希尔排序,归并排序,堆排序,快速排序等。
  • 另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。

排序算法的稳定性

排序算法稳定性的简单形式化定义为:如果Ai = Aj,对于每一次排序结果,Ai和Aj的相对次序保持不变。即排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。通俗地讲就是保证排序前后两个相等的数的相对顺序不变。
证明排序算法的稳定性,需要对算法进行分析从而得到稳定的特性

排序算法的时间、空间复杂度对比

在这里插入图片描述

冒泡排序(Bubble Sort)

冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。它重复地走访过要排序的元素,依次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。

改进:如果一轮冒泡过程中都没有发生交换,说明整个数组已经是有序的了,可以退出排序过程。
(鸡尾酒排序:稍微改进了冒泡排序,从低到高然后从高到低)

选择排序(Selection Sort)

选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。

插入排序(Insertion Sort)

插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌

对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。

插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,比如量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。

希尔排序(Shell Sort)

希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

归并排序(Merge Sort)

归并排序的实现分为递归实现与非递归(迭代)实现。
递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。
非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

堆排序(Heap Sort)

堆排序的基本思想是:

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
将堆顶元素与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,重复上一步得到次大值。
如此反复执行步骤2,3,便能得到一个有序序列了
参考链接:http://www.cnblogs.com/MOBIN/p/5374217.html

堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。

时间复杂度:O(nlogn)

初始化建堆时间O(n),参考

https://blog.csdn.net/XUNathan/article/details/27215781

https://blog.csdn.net/yuzhihui_no1/article/details/44258297

堆的插入和删除,都需要在堆的末尾n+1的位置插入元素

https://blog.csdn.net/hrn1216/article/details/51465270

快速排序(Quick Sort)

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:

从序列中挑出一个元素,作为"基准"(pivot).
把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。

基于比较的排序算法的时间复杂度下界为O(nlogn)的证明

对于n个待排序元素,在未比较时,可能的正确结果有n!种。

在经过一次比较后,其中两个元素的顺序被确定,所以可能的正确结果剩余n!/2种。

依次类推,直到经过m次比较,剩余可能性n!/(2^m)种。

直到n!/(2^m)<=1时,结果只剩余一种。此时的比较次数m为o(nlogn)次。

所以基于排序的比较算法,最优情况下,复杂度是o(nlogn)的

常用的非比较排序算法:计数排序,基数排序,桶排序。

计数排序(Counting Sort)

通俗地理解,例如有10个年龄不同的人,假如统计出有8个人的年龄不比小明大(即小于等于小明的年龄,这里也包括了小明),那么小明的年龄就排在第8位,通过这种思想可以确定每个人的位置,也就排好了序。当然,年龄一样时需要特殊处理(保证稳定性):通过反向填充目标数组,填充完毕后将对应的数字统计递减,可以确保计数排序的稳定性。

计数排序的步骤如下:

统计数组A中每个值A[i]出现的次数,存入C[A[i]]
从前向后,使数组C中的每个值等于其与前一项相加,这样数组C[A[i]]就变成了代表数组A中小于等于A[i]的元素个数
反向填充目标数组B:将数组元素A[i]放在数组B的第C[A[i]]个位置(下标为C[A[i]] - 1),每放一个元素就将C[A[i]]递减
因此对于数据范围很大的数组,计数排序需要大量时间和内存。例如:对0到99之间的数字进行排序,计数排序是最好的算法,然而计数排序并不适合按字母顺序排序人名。

将计数排序用在基数排序算法中,能够更有效的排序数据范围很大的数组。(例如,对于0~10000范围内的数,如果使用计数排序,需要O(10000),而使用基数排序的话,只需要O(4*10))

基数排序(Radix Sort)

基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机上的贡献。它是这样实现的:将所有待比较正整数统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始进行基数为10的计数排序,一直到最高位计数排序完后,数列就变成一个有序序列(利用了计数排序的稳定性)。

基数排序的时间复杂度是O(n * dn),其中n是待排序元素个数,dn是数字位数。这个时间复杂度不一定优于O(nlogn),dn的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小;dn决定了进行多少轮处理,而n是每轮处理的操作数目。

如果考虑和比较排序进行对照,基数排序的形式复杂度虽然不一定更小,但由于不进行比较,因此其基本操作的代价较小,而且如果适当的选择基数,dn一般不大于log n,所以基数排序一般要快过基于比较的排序,比如快速排序。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序并不是只能用于整数排序。

桶排序(Bucket Sort)

桶排序也叫箱排序。工作的原理是将数组元素映射到有限数量个桶里,利用计数排序可以定位桶的边界,每个桶再各自进行桶内排序(使用其它排序算法或以递归方式继续使用桶排序)。

桶排序不是比较排序,不受到O(nlogn)下限的影响,它是鸽巢排序的一种归纳结果,当所要排序的数组值分散均匀的时候,桶排序拥有线性的时间复杂度。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值