数据结构与算法 - 排序算法(下)
上一篇介绍了排序算法中的选择排序、冒泡排序、插入排序,这三种算法的平均情况时间复杂度都为$O(n^2)$,因此对于数据规模比较大的时候,就不适用了。本节介绍两种效率比较高的算法,归并排序、快速排序。
归并排序
归并排序的思想是:将要排序的数组分成两个子数组,将子数组进行排序,然后合并子数组。可以看出来,归并排序具有非常明显的分治策略特征。排序过程如下:
用递归式表示归并排序如下:
代码如下:
1 | --[[ |
下面分析一下归并排序:
空间复杂度:因为每次合并都需要分配额外的内存,所以所以最大需要 n 大小数组,因此空间复杂度为$O(n)$。
时间复杂度:采用主方法来计算时间复杂度:
- 首先将递归公式改成主方法要求形式:$T(n)=2T(n/2)+n$,其中 a=2,b=2,f(n)=n。
- 然后比较 f(n)=n,与$n^{\log_b^a}$=$n$,两者值相同,满足主定理的第二种情况,所以时间复杂度为:$O(n^{\log_b^a}\log{n})=O(n\log{n})$
快速排序
快速排序的思想是这样,对于待排序的数组A,选定数组中的任意元素作为分区点,然后将小于分区点的元素放到前面,大于分区点的放到后面。排序过程如下:
递归公式如下:
在上面递归式中,q代表分区点,分区点的选取方案有多种,可以选取头元素、尾元素作为分区点,但是更多的是采用随机分区点。根据递归公式写出递归代码:
1 | --- 分区点选取 |
这里需要解释一下分区算法的实现思路,它是将待分区的数组分为“已处理”、“未处理”两部分,已处理部分都是小于分区点的元素,其中 i 相当于已处理区的尾指针(已处理和未处理区的分界点,指向的元素属于未处理区),分区过程通过遍历未处理区,如果元素小于分区点,插入到已处理区(与 i 指向的元素进行交换),最后将分区点与 i 进行交换。
然后分析一下快速排序:
空间复杂度:快速排序也是原址排序,所以空间复杂度为$O(1)$
是否稳定排序:因为分区过程中,涉及到元素的交换,所以不是稳定的排序
利用主方法还计算快排算法的时间复杂度:
- 首先将递归式写成主方法要求的形式:$T(n)=2T(n/2)+n$
- 根据上式可知,a = 2,b = 2,f(n)=n。
- 比较$f(n)=n$,$n^{\log_b^a}=n$,两者相等,所以时间复杂度为:$n{\log}n$
对于快排来说,分区点的选取尤其重要,如果分区点选取不合适,可能造成每次分区都需要遍历整个数组,时间复杂度就会退化为$O(n^2)$,上面采用随机选取分区点,就是为了降低这种可能性。
总结
| 算法 | 时间复杂度 | 空间复杂度 | 是否稳定排序 |
|---|---|---|---|
| 归并排序 | $n\log{n}$ | $O(n)$ | 是 |
| 快速排序 | $n\log{n}$ | $O(1)$ | 否 |