一个推荐的博客,里面有详细的动图介绍。
各排序复杂度
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 快速排序 | $O(n\log _2n)$ | $O(n^2)$ | $O(n\log _2n)$ | $O(\log _2n)$ | 不稳定 |
| 冒泡排序 | $O(n^2)$ | $O(n^2)$ | $O(n)$ | $O(1)$ | 稳定 |
| 插入排序 | $O(n^2)$ | $O(n^2)$ | $O(n)$ | $O(1)$ | 稳定 |
| 选择排序 | $O(n^2)$ | $O(n^2)$ | $O(n^2)$ | $O(1)$ | 不稳定 |
| 归并排序 | $O(n\log _2n)$ | $O(n\log _2n)$ | $O(n\log _2n)$ | $O(n)$ | 稳定 |
| 希尔排序 | $O(n^{1.3})$ | $O(n^2)$ | $O(n)$ | $O(1)$ | 不稳定 |
| 堆排序 | $O(n\log _2n)$ | $O(n\log _2n)$ | $O(n\log _2n)$ | $O(1)$ | 不稳定 |
快速排序
思想:
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分进行排序,也就是分治法的思想。
步骤:
- 从数列中挑选出一个元素,作为基准
- 重新遍历数列,将比基准小的放在前面,比基准大的放在后面,相同的可以放在任意一边
- 然后对刚才左边的和右边的,分别递归上面的操作
不稳定,这个时候不是双重循环,时间复杂度O(nlogn).
1 | # ------ coding:utf-8 ----- |
冒泡排序
思想:
重复走访待排的序列,一次比较两个元素,比如它们之间的顺序错误了,就把它们进行交换。冒泡排序就是把小的元素往前调,把大的元素往后调。经过一次循环之后,最大的值将出现在最后。注意的是相邻的两个元素进行比较,而且是否需要交换也发生在这两个元素之间。
步骤:
- 比较相邻元素,如果第一个比第二个大,则交换它们
- 重复,直到序列末尾,这个时候序列最后的元素就是最大的数
重复,直到排序完成
当此次循环中,没有元素交换的时候,就停止,代表排序完成
如果两个元素相等,是不会去交换位置的。所以即使通过前面的两辆交换把两个元素放在的一起,也不会交换他们的位置,所以冒泡排序是稳定的。双重循环,时间复杂度O(n2).
1 | # ------- coding:utf-8 ---- |
插入排序
思想:
通过构建有序序列,对于未排序数据,在已排序序列中,从后向钱扫描,找到相应的位置进行并插入。
步骤:
- 从第一个元素开始,这个时候是一个有序序列
- 取出下一个元素,在已经排序的序列中,从后向前扫描,如果有序序列中的当前元素大于待排元素,则该元素后移一个位置,直到找到有序序列中的元素小于等于待排元素,就在该位置进行插入(可能找不到这样的元素,则就在最前的位置插入)
- 重复上面的步骤,直到没有待排元素
相等元素的前后顺序没有改变,所以插入排序是稳定的。双重循环,时间复杂度O(n2).
1 | # ------ coding:utf-8 ----- |
选择排序
思想:
首先在未排序的序列中找到最小的元素,存放在已排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列末尾。
选择排序即是给每个位置选择待排序元素中当前最小的元素。比如给第一个位置选择最小的,在剩余元素里面给第二个位置选择次小的。以此类推,直到第n-1个元素,第n个元素就不用选择了。
举例:5 8 5 2 9,首先会将5与2进行交换,那么两个5的顺序就交换了,所以,选择排序不稳定。
双重循环,时间复杂度O(n2).
1 | # ------ coding:utf-8 ------- |
归并排序
思想:
采用分治法,将两个已经有序的序列合并成一个有序序列,得到完全有序的序列。即先使每个子序列有序,再使自序列之间有序。若将两个有序表合并成一个有序表,成为2-路归并。
步骤:
- 将长度为 n 的序列分成两个长度为 n/2 的子序列
- 将这两个自序列分别采用归并排序
将两个排序好的自序列合并成一个最终的排序序列
归并排序最初的状态,可以看成是 n 个长度为 1 的有序自序列。
在1个或2个元素时,1个元素不会交换,2个元素如果大小相等的话也不会交换,所以是稳定的
这个时候不是双重循环,时间复杂度O(nlogn)
1 | # -------- coding:utf-8 ------ |
希尔排序
思想:
希尔排序又叫做缩小增量排序。是简单插入排序的改进版,不同之处在于,它会优选比较距离较远的元素。
步骤:
- 选择增量序列:如 5 3 2 1
- 按增量序列个数 k,进行 k 趟排序,上面这个例子就是 4 趟排序
每趟排序,根据对应的增量,将器分成若干个长度为 m 的子序列,然后对各个子表进行直接插入排序
通常在实现的过程中,可以不用指定增量序列,初始增量 step = len(nums)/2,后面每一次 step/= 2.
1 | def shellSort(nums): |
因为相同的数在一次 step 中,可能不在同一个自序列,因此可能在这个过程中位置发生改变,所以希尔排序是不稳定的。