概述
归并排序是一种时间复杂度为$ O (N\log N) $的排序算法,使用的是 分治 的策略。
算法的基本操作是合并两个已经排序的序列,因为两个序列是已经排序了的,所以将它们按顺序复制到第三个序列中,可以通过一次遍历完成。
所以将原序列使用递归不断的分成两个子序列,直至序列中是元素个数为1,此时序列为已经排序完成的,将两个子序列合并,得到一个排序完成的序列,再继续合并,直至整个序列都是已排序的。
算法实现
算法实现主要包含两个过程:将序列从指定的开始到结束位置的子序列排序,合并两个有序的序列
算法是用递归实现的,递归的边界为:指定的结束位置和和结束位置相同,此时子序列只有一个元素,显然子序列为已排序的。
完整实现代码参见:MergeSort
子序列排序
1 | private static <T extends Comparable<? super T>> void mergeSort(T[] eles, T[] temp, int begin, int end) { |
合并子序列
1 | private static <T extends Comparable<? super T>> void merge(T[] eles, T[] temp, int leftPos, int rightPos, int rightEnd) { |
算法入口
1 | public static <T extends Comparable<? super T>> void sort(T[] eles) { |
算法分析
假设为题规模$N$为2的幂,这样我们可以将它分为连个相等的部分。对于$N = 1$,归并排序所用的时间为常数,我们将它记为1(这个常数是多少并不会影响到最终得到的时间复杂度,这里使用1方便于计算)。
对于N归并排序的时间等于完成两个大小为 $N/2$ 的归并排序所用的时间加上合并的时间,合并所用的时间为线性的。所以有下列等式:
由于$N$为2的幂,所以将等式1中的$N$用$N/2$替换,并将等式两侧同时乘以2,则可以得到
从而得到
再将$N/4$带入等式1,同样能得到
所以我们有
以此类推,我们将能得到
其中 $k = \log N$, 所以
因此归并排序的时间复杂度为 $O(N\log N)$。
此证明中令$N$为2的幂,可以简化计算,当$N$不为2的幂时计算略有不同,但得到的时间复杂度仍未$O(N\log N)$。
思考
- 归并排序的非递归实现
- 如何不使用额外的内存空间完成排序
参考
MarkAllenWeiss. 数据结构与算法分析:Java语言描述-第3版[M]. 机械工业出版社, 2016, 193-198