归并排序介绍
● 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
● 归并排序(MERGE-SORT): 是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。
● 归并排序其实要做两件事:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
● 归并排序(Merge Sort): 就是利用归并思想对数列进行排序。根据具体的实现,归并排序包括”从上往下”和”从下往上”2种方式。
从下往上的归并排序(自顶向上归并排序):
(1)将待排序的数列分成若干个长度为1的子数列,
(2)然后将这些数列两两合并;得到若干个长度为2的有序数列
(3)再将这些数列两两合并;得到若干个长度为4的有序数列,
(4)再将它们两两合并;得到若干个长度为8的有序数列,
(5)以此类推 ,直到,归并的长度大于整个数组的长度,此时整个数组有序。需要注意的是数组按照归并长度划分,最后一个子数组可能不满足长度要求,这个情况需要特殊处理。自顶向下的归并排序算法一般用递归来实现,而自底向上可以用循环来实现。
从上往下的归并排序(自顶向下):它与”从下往上”在排序上是反方向的。它基本包括3步:
① 分解 – 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
② 求解 – 递归地对两个子区间a[low…mid] 和 a[mid+1…high]进行归并排序。递归的终结条件是子区间长度为1。
③ 合并 – 将已排序的两个子区间a[low…mid]和 a[mid+1…high]归并为一个有序的区间a[low…high]。
自顶向下的排序算法就是把数组元素不断的二分,直到子数组的元素个数为一个,因为这个时候子数组必
定是已有序的,然后将两个有序的序列合并成一个新的有序的序列,
两个新的有序序列又可以合并成另一个新的有序序列,以此类推,直到合并成一个有序的数组。
下面的图片很清晰的反映了”从下往上”(自顶向上)和”从上往下”(自顶向下)的归并排序的区别。
从上往下的归并排序(自顶向下)采用了递归的方式实现。它的原理非常简单,如下图:
通过”从上往下的归并排序”来对数组{80,30,60,40,20,10,50,70}进行排序时:
1. 将数组{80,30,60,40,20,10,50,70}看作由两个有序的子数组{80,30,60,40}和{20,10,50,70}组成。
对两个有序子树组进行排序即可。
将子数组{80,30,60,40}看作由两个有序的子数组{80,30}和{60,40}组成。
将子数组{20,10,50,70}看作由两个有序的子数组{20,10}和{50,70}组成。3. 将子数组{80,30}看作由两个有序的子数组{80}和{30}组成。 将子数组{60,40}看作由两个有序的子数组{60}和{40}组成。 将子数组{20,10}看作由两个有序的子数组{20}和{10}组成。 将子数组{50,70}看作由两个有序的子数组{50}和{70}组成。
从下往上的归并排序(自顶向上)的思想正好与”从上往下的归并排序”相反。如下图:
通过”从下往上的归并排序”来对数组{80,30,60,40,20,10,50,70}进行排序时:
1. 将数组{80,30,60,40,20,10,50,70}看作由8个有序的子数组{80},{30},{60},{40},{20},{10},{50}和{70}组成。
2. 将这8个有序的子数列两两合并。得到4个有序的子树列{30,80},{40,60},{10,20}和{50,70}。
3. 将这4个有序的子数列两两合并。得到2个有序的子树列{30,40,60,80}和{10,20,50,70}。
4. 将这2个有序的子数列两两合并。得到1个有序的子树列{10,20,30,40,50,60,70,80}。
可以看到这种结构很像一棵完全二叉树,分阶段可以理解为就是递归拆分子序列的过程,递归深度为
合并相邻有序子序列
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
归并排序算法的性能
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?
归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的可以得出它的时间复杂度是 O(n*log2n)。
归并排序稳定性
归并排序是稳定的算法,它满足稳定算法的定义。
算法稳定性 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
空间复杂度
由前面的算法说明可知,算法处理过程中,需要一个大小为n的临时存储空间用以保存合并序列。
归并排序和堆排序、快速排序的比较
若从空间复杂度来考虑:首选堆排序,其次是快速排序,最后是归并排序。
若从稳定性来考虑,应选取归并排序,因为堆排序和快速排序都是不稳定的。
若从平均情况下的排序速度考虑,应该选择快速排序。