什么是归并排序,顾名思义,归并简单理解成合并多个序列,归并排序也就是归并多个有序序列。这里讨论的是二路归并排序,二路是指将未排序的序列一分为二,分别排序后合二为一(也就是归并两个序列)。实现归并排序有两种方法,一种是递归,而是非递归。

(二路)归并排序 - 图1

递归方法

  1. public static void sort(int[] arr) {
  2. mergeSort(arr, 0, arr.length - 1);
  3. }
  4. public static void mergeSort(int[] arr, int low, int high) {
  5. int m;
  6. if (low < high) {
  7. m = (low + high) / 2;
  8. mergeSort(arr, low, m);
  9. mergeSort(arr, m + 1, high);
  10. merge(arr, low, m, high);
  11. }
  12. }
  1. public static void merge(int[] arr, int low, int m, int high) {
  2. int k, j;
  3. int[] temp = new int[arr.length];
  4. System.arraycopy(arr, low, temp, low, high - low + 1);
  5. for (k = low, j = m + 1; low <= m && j <= high; k++) {
  6. if (temp[low] < temp[j]) {
  7. arr[k] = temp[low++];
  8. } else {
  9. arr[k] = temp[j++];
  10. }
  11. }
  12. if (low > m) {
  13. System.arraycopy(temp, j, arr, k, high - j + 1);
  14. }
  15. if (j > high) {
  16. System.arraycopy(temp, low, arr, k, m - low + 1);
  17. }
  18. }

测试

  1. public static void main(String[] args) throws IOException {
  2. int[] arr = {1, 0, 12, 45, 21, 78, 628, 45, 36, 97, 15, 65, 45, 24, 10, 74, 58, 69, 25, 46, 36, 39, 48, 43, 19, 18};
  3. int[] temp = new int[arr.length];
  4. sort(arr);
  5. for (int i : arr) {
  6. System.out.println(i);
  7. }
  8. }

缺点: merge方法第4行的System.arraycopy(arr, low, temp, low, high - low + 1),每一层递归复制n次,时间复杂度 nlogn

优化

每一次递归都创建一个tempArr2对象用于保存本次排序的未归并结果,然后传递给归并方法,避免以上所说的缺点,时间复杂度为n由下面第11行代码tempArr[low] = arr[low]可以看出来一共只执行n次,相比每一层递归都复制n次,这个优化对于提高算法效率还是很重要。

  1. public static void mergeSort(int[] arr, int[] tempArr, int low, int high) {
  2. int[] tempArr2 = new int[arr.length];
  3. int m;
  4. if (low < high) {
  5. m = (low + high) / 2;
  6. mergeSort(arr, tempArr2, low, m);
  7. mergeSort(arr, tempArr2, m + 1, high);
  8. merge(tempArr2, tempArr, low, m, high);
  9. } else {
  10. tempArr[low] = arr[low];
  11. }
  12. }
  13. public static void merge(int[] arr, int[] temp, int low, int m, int high) {
  14. int k, j;
  15. for (k = low, j = m + 1; low <= m && j <= high; k++) {
  16. if (arr[low] < arr[j]) {
  17. temp[k] = arr[low++];
  18. } else {
  19. temp[k] = arr[j++];
  20. }
  21. }
  22. if (low > m) {
  23. System.arraycopy(arr, j, temp, k, high - j + 1);
  24. }
  25. if (j > high) {
  26. System.arraycopy(arr, low, temp, k, m - low + 1);
  27. }
  28. }

非递归方法

递归方法实质是递归到最底层然后逐层向上归并,那么也可以一开始就直接从最底层逐层向上归并,这样更加省时间,免去了递归的损耗。

  1. public static void sort(int[] arr) {
  2. int k = 1;
  3. int[] temp = new int[arr.length];
  4. while (k < arr.length) {
  5. mergePass(arr, temp, k, arr.length);
  6. k = 2 * k;
  7. mergePass(temp, arr, k, arr.length);
  8. k = 2 * k;
  9. }
  10. }
  11. public static void mergePass(int[] SR, int[] TR, int s, int n) {
  12. int i = 0;
  13. int j;
  14. while (i + 2 * s < n) {
  15. merge(SR, TR, i, i + s - 1, i + 2 * s - 1);
  16. i = i + 2 * s;
  17. }
  18. if (i + s - 1 < n - 1) {
  19. merge(SR, TR, i, i + s - 1, n - 1);
  20. } else {
  21. for (j = i; j < n; j++)
  22. TR[j] = SR[j];
  23. }
  24. }
  25. public static void merge(int[] arr, int[] temp, int low, int m, int high) {
  26. int k, j;
  27. for (k = low, j = m + 1; low <= m && j <= high; k++) {
  28. if (arr[low] < arr[j]) {
  29. temp[k] = arr[low++];
  30. } else {
  31. temp[k] = arr[j++];
  32. }
  33. }
  34. if (low > m) {
  35. System.arraycopy(arr, j, temp, k, high - j + 1);
  36. }
  37. if (j > high) {
  38. System.arraycopy(arr, low, temp, k, m - low + 1);
  39. }
  40. }

总结

在递归方法的第一个算法里面,为了省去方法的参数直接将排序结果作用于原数组,这就导致每一归并都要进行复制,比传递排序序列的方法多了一步,导致没必要的耗时,所以在解决之类问题时,没有必要一味追求程序的简洁,而是应该考虑到算法的效率。