归并排序核心思想:递归过程,使得前1/2和后1/2分别有序,然后对两个有序序列进行合并。从递归的逆过程看,就是两两合并,最终达到有序。
递归逆过程举例:
【3】【1】【5】【8】【7】【4】【2】【6】
【1,3】【5,8】【7,4】【2,6】
【1,3,5,8】【2,4,6,7】
【1,2,3,4,5,6,7,8】
对于归并排序来说,合并过程是算法中的核心步骤,我们不多说了,来看看代码。
import java.util.Arrays;
public class MergeSort {
public void sort(int[] s, int low, int high) {
if (high > low) {
int mid = (low + high) / 2; //将数组分割成两段
sort(s, low, mid); //对前半段进行递归排序
sort(s, mid + 1, high); //对后半段进行递归归并排序
merge(s, low, mid, high); //在执行完对前后两段的排序后进行归并操作
}
}
public void merge(int[] s, int low, int mid, int high) {
int[] tmp = new int[high - low + 1];//新建一个大小为high - low + 1的合并数组
int i = low, j = mid + 1;//两个指针i和j,分别用来遍历前后半段
int k = 0;//k为合并数组的指针
while (i <= mid && j <= high) {
//从i和j开始比较两段的值,取较小值放入合并数组中
if (s[i] <= s[j]) {
tmp[k++] = s[i++];
} else {
tmp[k++] = s[j++];
}
}
//如果某段没有遍历完,则将剩余部分复制到合并数组中;这种情况发生在某段数值都偏大/小时
while (i <= mid) {
tmp[k++] = s[i++];
}
while (j <= high) {
tmp[k++] = s[j++];
}
//然后将合并数组复制到原数组中
for (k = 0, i = low; i <= high; i++, k++) {
s[i] = tmp[k];
}
}
public static void main(String[] args) {
int[] s = new int[] { 3, 1, 5, 10, 7, 4, 3, 2, 1, 6 };
MergeSort ms = new MergeSort();
ms.sort1(s, 0, s.length - 1);
System.out.println(Arrays.toString(s));
}
}
归并排序的实现其实是比较好理解的,核心是维护归并过程中的归并数组,由于归并数组的存在,使得归并排序的空间复杂度为
归并排序在写法框架上和快速排序和类似,但是有一些本质上的区别。
- 快速排序是在向下递归的过程中使得序列有序。每次sort开始时先执行partition使得 右>左
- 归并排序是在递归回退时进行子序列合并,不断合并子序列,最终使序列整体有序
看到这,不知道你有没有想到很相似的过程。没错,二叉树的递归先序遍历和后序遍历,你能从快速排序和归并排序的递归写法上找到他们的影子。