1. 简介

实际上学习归并排序最重要的不是为了把这个排序算法学会, 而是学会归并排序用到的思想: 分治法

《算法导论》这本书介绍的第二个算法就是归并排序
书中借由归并排序向大家介绍了一种算法设计的范式分治法

1.1 分治法的思想

将原问题分解为几个规模较小但类似于原问题的子问题, 递归地求解这些子问题, 然后再合并这些子问题的解, 建立原问题的解
分治模式在每层递归时都有三个步骤:

  1. 分解: 将原问题分解为若干子问题
  2. 解决: 解决这些子问题
  3. 合并: 将子问题的解合并, 形成原问题的解

1.2 归并排序的思想

归并排序是个非常典型的分治法排序算法
每一层递归的操作如下:

  1. 分解: 将待排序的元素分解成两份
  2. 解决: 使用归并排序递归的排序两个子序列
  3. 合并: 合并两个已排序的子序列, 使得待排序的元素有序

2. 实现

Java实现

  1. public static int[] sort(int[] sourceArray) {
  2. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  3. if (arr.length < 2) {
  4. return arr;
  5. }
  6. //分解
  7. int middle = (int) Math.floor(arr.length / 2);
  8. int[] left = Arrays.copyOfRange(arr, 0, middle);
  9. int[] right = Arrays.copyOfRange(arr, middle, arr.length);
  10. //sort(): 解决
  11. //merge(): 合并
  12. return merge(sort(left), sort(right));
  13. }
  14. private static int[] merge(int[] left, int[] right) {
  15. int[] result = new int[left.length + right.length];
  16. int i = 0;
  17. while (left.length > 0 && right.length > 0) {
  18. if (left[0] <= right[0]) {
  19. result[i++] = left[0];
  20. left = Arrays.copyOfRange(left, 1, left.length);
  21. } else {
  22. result[i++] = right[0];
  23. right = Arrays.copyOfRange(right, 1, right.length);
  24. }
  25. }
  26. while (left.length > 0) {
  27. result[i++] = left[0];
  28. left = Arrays.copyOfRange(left, 1, left.length);
  29. }
  30. while (right.length > 0) {
  31. result[i++] = right[0];
  32. right = Arrays.copyOfRange(right, 1, right.length);
  33. }
  34. return result;
  35. }

3. 简单分析

时间复杂度

  • 归并排序的时间复杂度为O(nlog(n))

image.png

  • 树高 logn, 每一层要处理的元素有n个, 所以总的时间复杂度为 nlogn

空间复杂度

  • 由于排序的每个子任务都需要使用额外的内存空间, 所以控件复杂度是O(n)

稳定性

  • 归并排序不会因为元素的排列顺序而影响排序的速度
  • 至今JDK中都还有归并排序存在, 就足以说明归并排序的算法足够稳定

image.png
感兴趣的可以看看java.util.Arrays#sort(java.lang.Object[])这个排序算法的注释

  1. * <p>This sort is guaranteed to be <i>stable</i>: equal elements will
  2. * not be reordered as a result of the sort.