Python算法和数据结构(二)——各大排序算法的时空复杂度比较
一、各大排序算法的时空复杂度表格
排序算法 | 最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
---|---|---|---|---|
冒泡排序 | O(n2) | O(n2) | 稳定 | O(1) |
选择排序 | O(n2) | O(n2) | 不稳定 | O(1) |
插入排序 | O(n2) | O(n2) | 稳定 | O(1) |
快速排序 | O(n2) | O(n*log n) | 不稳定 | O(log n) ~ O(n) |
堆排序 | O(n*log n) | O(n*log n) | 不稳定 | O(1) |
二、对一些问题的注释
1、冒泡排序
对于冒泡排序,我们需要注意的点是始终对相邻的两个数作比较和交换操作。
2、选择排序
选择排序的中心思想是找到目前所需要的最大(或者最小的数字),然后将其放到相应的位置上去。它做的是记录位置,每遍历一次只做一次交换操作(或许是什么都不做)
**注意:**为什么说选择排序是不稳定的呢?
所谓的稳定性指的是尽量在不懂左右的数组内数的顺序
例如:数组[5,2,5,1,9],我们使用从小到大选择排序的话,第一次遍历选择了数字1,将第一个5跟1调换位置,这个时候,会发现本来在第二个5前面的5跑到后面去了,这个就叫做不稳定。(因为假设我们现在排序的不是单纯的数字,而是带有自己的很多属性的5,我们本来已经根据属性对两个5进行了先后排序,那么刚刚的操作却使我们改变了他们原本的顺序,因此也就是不稳定了)
3、插入排序
插入排序跟选择排序很相似,我们创建一个新的数组,然后不断地从旧的数组中提取一个最大(或者最小的)数字出来,插入到新的数组中。
4、快速排序
这里我们把快速排序和归并排序放一起看
排序算法 | 最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
---|---|---|---|---|
快速排序 | O(n2) | O(n*log n) | 不稳定 | O(log n) ~ O(n) |
归并排序 | O(n*log n) | O(n*log n) | 稳定 | O(n) |
快排很简单,有很多种实现的方式,而且不同的实现方式可以改变快排的稳定性,就像是我之前在 《对数器》一文中实现快排的方法就是稳定的快排方法。
归并的本质就是不断地二分,分到每两个数字一个单元,然后再进行归并
快排的改进:
- 对于快排当有好几个数字相同的情况,基础的快排我们每次只能排序一个数字(flag),而对于那些相等的数字却没有实现一起排序,这其实是很浪费的。改进的方式请看我的往期文章 《荷兰国旗问题》,可以进行相同数字的整合排序。
- 对于有序数组反向排序(比如从大到小的数组排成从小到大),基础的快排会导致我们获得的时间复杂度最高,那么如何改进呢?根据基础快排,我们一般会选择最后的数为flag,然后做排序操作,所以会发现这个时间复杂度之所以如此之高的原因的本质在于我们每次只是把原来的数组排成了两个部分(一般来说均匀分成三部分才是达到了性能最佳O(n*log n)),所以,我们的解决方应该是能够随机选择出来一个flag进行排序,也就是我们常说的“随机快排”了
- 快拍的改进还有很多,像Python 系统内置的 sort 方法其实就是集合了各种各样的排序的优点放在一起而做出的排序。
5、堆排序
下面给出堆排序的图解(下图截自(https://www.cnblogs.com/chengxiao/p/6129630.html)):
我们来构造一个大顶堆:
至于堆排序的Python实现将在之后的文章里讲到啦,本期文章只做概述,就到这里啦
嘿嘿,I am very glateful that 你看到这里了哦~下回再见ヾ(o◕∀◕)ノヾ
Thx