归并排序的时间复杂度任何情况下都是 O(nlogn)
,看起来非常优秀。(待会儿你会发现,即便是快速排序,最坏情况下,时间复杂度也是 O(n^2)。)但是,归并排序并没有像快排那样,应用广泛,这是为什么呢?因为它有一个致命的“弱点”,那就是归并排序不是原地排序算法
。
这是因为归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间
。这一点你应该很容易理解。那我现在问你,归并排序的空间复杂度到底是多少呢?是 O(n),还是 O(nlogn),应该如何分析呢?
如果我们继续按照分析递归时间复杂度的方法,通过递推公式来求解,那整个归并过程需要的空间复杂度就是 O(nlogn)。不过,类似分析时间复杂度那样来分析空间复杂度,这个思路对吗?
实际上,递归代码的空间复杂度并不能像时间复杂度那样累加
。刚刚我们忘记了最重要的一点,那就是,尽管每次合并操作都需要申请额外的内存空间,但在合并完成之后,临时开辟的内存空间就被释放掉了。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)
。
思考
- 如果数组元素个数n,归并排序过程需要多少个临时数组呢?
2+4+8+....+n/2=?
- 归并排序的空间复杂度为何不能累加?
- 归并排序为何需要临时数组?
合并的时候暂存元素,为何不能原位排序,因为需要的临时数组不止一个
- 归并排序的排序是在拆分还是在合并时进行的?
总结
- 归并排序的空间复杂度是
O(n)
归并排序应用不如快排广泛
,因为需要更多的内存空间- 递归代码的
空间复杂度
并不能像时间复杂度
那样累加 归并排序的排序是在每一次合并时进行的
,也就是说每个临时数组最终都是有序的