# 排序 - 归并排序 (Merge Sort)
# 归并排序介绍
根据具体的实现,归并排序包括 "从上往下" 和 "从下往上"2 种方式。
# 从下往上的归并排序
将待排序的数列分成若干个长度为 1 的子数列,然后将这些数列两两合并;得到若干个长度为 2 的有序数列,再将这些数列两两合并;得到若干个长度为 4 的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。
# 从上往下的归并排序
它与 "从下往上" 在排序上是反方向的。它基本包括 3 步:
分解– 将当前区间一分为二,即求分裂点 mid = (low + high)/2;求解– 递归地对两个子区间 a [low…mid] 和 a [mid+1…high] 进行归并排序。递归的终结条件是子区间长度为 1。合并– 将已排序的两个子区间 a [low…mid] 和 a [mid+1…high] 归并为一个有序的区间 a [low…high]。

# 归并排序实现
# 从上往下的归并排序
从上往下的归并排序采用了递归的方式实现。它的原理非常简单,如下图:
通过 "从上往下的归并排序" 来对数组 {90,80,70,10,20,50,60,40} 进行排序时:
- 将数组 {90,80,70,10,20,50,60,40} 看作由两个有序的子数组 {90,80,70,10} 和 {20,50,60,40} 组成。对两个有序子树组进行排序即可。
- 将子数组 {90,80,70,10} 看作由两个有序的子数组 {90,80} 和 {70,10} 组成。
- 将子数组 {20,50,60,40} 看作由两个有序的子数组 {20,50} 和 {60,40} 组成。
- 将子数组 {90,80} 看作由两个有序的子数组 {90} 和 {80} 组成。
- 将子数组 {70,10} 看作由两个有序的子数组 {70} 和 {10} 组成。
- 将子数组 {20,50} 看作由两个有序的子数组 {20} 和 {50} 组成。
- 将子数组 {60,40} 看作由两个有序的子数组 {60} 和 {40} 组成。
# 从下往上的归并排序
从下往上的归并排序的思想正好与 "从下往上的归并排序" 相反。如下图:

通过 "从下往上的归并排序" 来对数组 {90,80,70,10,20,50,60,40} 进行排序时:
- 将数组 {90,80,70,10,20,50,60,40} 看作由 8 个有序的子数组 {90},{80},{70},{10},{20},{50},{60} 和 {40} 组成。
- 将这 8 个有序的子数列两两合并。得到 4 个有序的子树列 {80,90},{10,70},{20,50} 和 {40,60}。
- 将这 4 个有序的子数列两两合并。得到 2 个有序的子树列 {10,70,80,90} 和 {20,40,50,60}。
- 将这 2 个有序的子数列两两合并。得到 1 个有序的子树列 {90,80,70,10,20,50,60,40}。
# 排序原理:
1. 尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是 1 为止。
2. 将相邻的两个子组进行合并成一个有序的大组;
3. 不断的重复步骤 2,直到最终只有一个组为止。
# 归并排序 API 设计
| 类名 | mergeSort |
|---|---|
| 构造方法 | Merge ():创建 Merge 对象 |
| 成员方法 | 1.public static void sort (int [] a):对数组内的元素进行排序 2.private static void sort (int [] a, int lo, int hi):对数组 a 中从索引 lo 到索引 hi 之间的元素进 3.private static void merge (int [] a, int lo, int mid, int hi): 从索引 lo 到所以 mid 为一个子组,从索引 mid+1 到索引 hi 为另一个子组,把数组 a 中的这两个子组的数据合并成一个有序的大组(从从索引 lo 到索引 hi)4.private static boolean less (int v,int w): 判断 v 是否小于 w |
| 成员变量 | 1.private static int [] assist:完成归并操作需要的辅助数组 |



# 归并排序的时间复杂度和稳定性
# 归并排序时间复杂度
归并排序的时间复杂度是 O (N*lgN)。
假设被排序的数列中有 N 个数。遍历一趟的时间复杂度是 O (N),需要遍历多少次呢?归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的可以得出它的时间复杂度是 O (N*lgN)。
# 归并排序稳定性
归并排序是稳定的算法,它满足稳定算法的定义。
算法稳定性 – 假设在数列中存在 a [i]=a [j],若在排序之前,a [i] 在 a [j] 前面;并且排序之后,a [i] 仍然在 a [j] 前面。则这个排序算法是稳定的!
代码实现:
package com.w.winterVacation.sort; | |
/** | |
* 归并排序 | |
* | |
* @author wspstart | |
* @create 2023-01-26 17:05 | |
*/ | |
public class MergeSort { | |
// 归并所需要的辅助数组 | |
private static int[] assist; | |
/* | |
比较 v 是否小于 w | |
*/ | |
private static boolean less(int v, int w) { | |
return v <= w; | |
} | |
/* | |
对数组中 arr 的元素进行排序 | |
*/ | |
public static void sort(int[] arr) { | |
// 1、初始化辅助数组 assist; | |
assist = new int[arr.length]; | |
// 2、定义一个 left 变量和 right 变量,分别记录数组中最小的索引和最大的索引 | |
int left = 0; | |
int right = arr.length - 1; | |
// 3、调用 sort 重载方法完成数组 arr 中,从索引 left 到索引 right 的元素的排序 | |
sort(arr, left, right); | |
} | |
/* | |
对数组 arr 中从 left 到 right 中的元素排序 | |
*/ | |
private static void sort(int[] arr, int left, int right) { | |
// 对传入的数据进行安全性校验 | |
if (right <= left) { | |
return; | |
} | |
// 对数据进行分组 | |
int mid = left + ((right - left) >> 1); | |
// 对每一组数据进行排序 | |
sort(arr, left, mid); | |
sort(arr, mid + 1, right); | |
merge(arr, left, right, mid); | |
} | |
/** | |
* 归并,归并的过程中需要对数据进行排序 | |
* | |
* @param arr 排序的原始数组 | |
* @param left 左边有序序列的初始索引 | |
* @param right 右边索引 | |
* @param mid 中间索引 | |
*/ | |
private static void merge(int[] arr, int left, int right, int mid) { | |
// 定义三个指针 | |
int i = left; // 表示 assist 数组中已有的有效数据的索引 | |
int p1 = left; // 左子组的第一个位置 | |
int p2 = mid + 1; // 右子组的第一个位置 | |
// 遍历,移动 p1 指针和 p2 指针,比较对应的索引处的值,找出小的那个,放到辅助数组的对应索引处 | |
while (p1 <= mid && p2 <= right) { | |
// 比较对应索引处的值 | |
if (less(arr[p1], arr[p2])) { | |
assist[i++] = arr[p1++]; | |
} else { | |
assist[i++] = arr[p2++]; | |
} | |
} | |
// 出了第一个循环之后,两个子组中的某一个子组中的元素已经遍历完成了。 | |
// 遍历,如果 p2 的指针没有走完,那么顺序移动 p2 指针,把对应的元素放到辅助数组的对应索引处 (下面两个循环只会走一个) | |
while (p1 <= mid) { | |
assist[i++] = arr[p1++]; | |
} | |
while (p2 <= right) { | |
assist[i++] = arr[p2++]; | |
} | |
// 把辅助数组中的元素拷贝到原数组中 | |
for (int index = left; index <= right; index++) { | |
arr[index] = assist[index]; | |
} | |
} | |
} |