算法解析

归并排序体现了 “分而治之” 的算法思想,具体为:
「分」: 不断将数组从 中点位置 划分开,将原数组的排序问题转化为子数组的排序问题;
「治」: 划分到子数组长度为 1 时,开始向上合并,不断将 左右两个较短排序数组 合并为 一个较长排序数组,直至合并至原数组时完成排序;
如下图所示,为数组 [7,3,2,6,0,1,5,4] 的归并排序过程。
image.png

算法流程

递归划分:
计算数组中点 m ,递归划分左子数组 merge_sort(l, m) 和右子数组 merge_sort(m + 1, r) ;
当 l≥r 时,代表子数组长度为 1 或 0 ,此时 终止划分 ,开始合并;
合并子数组:
暂存数组 nums 闭区间 [l, r] 内的元素至辅助数组 tmp ;
循环合并: 设置双指针 i , j 分别指向 tmp 的左 / 右子数组的首元素;
注意: nums 子数组的左边界、中点、右边界分别为 l , m , r ,而辅助数组 tmp 中的对应索引为 0 , m - l , r−l ;
当 i == m - l + 1 时: 代表左子数组已合并完,因此添加右子数组元素 tmp[j] ,并执行 j = j + 1 ;
否则,当 j == r - l + 1 时: 代表右子数组已合并完,因此添加左子数组元素 tmp[i] ,并执行 i = i + 1 ;
否则,当 tmp[i]≤tmp[j] 时: 添加左子数组元素 tmp[i] ,并执行 i = i + 1 ;
否则(即当 tmp[i]>tmp[j] 时): 添加右子数组元素 tmp[j]tmp[j] ,并执行 j = j + 1 ;如下动图所示,为数组 [7,3,2,6] 的归并排序过程。

  1. /**
  2. * 归并排序
  3. */
  4. public class MergeSortTest {
  5. public static void main(String[] args) {
  6. // 调用
  7. int[] nums = {3, 4, 1, 5, 2, 1};
  8. MergeSortTest mergeSortTest = new MergeSortTest();
  9. mergeSortTest.mergeSort(nums, 0, nums.length - 1);
  10. for (int num : nums) {
  11. System.out.print(num);
  12. }
  13. }
  14. /**
  15. * 归并排序
  16. *
  17. * @param nums
  18. * @param l
  19. * @param r
  20. */
  21. void mergeSort(int[] nums, int l, int r) {
  22. // 终止条件
  23. if (l >= r) {
  24. return;
  25. }
  26. int mid = (l + r) / 2;
  27. mergeSort(nums, l, mid);
  28. mergeSort(nums, mid + 1, r);
  29. int[] tmp = new int[r - l + 1]; // 暂时保存元素
  30. for (int i = l; i <= r; i++) {
  31. tmp[i - l] = nums[i];
  32. }
  33. int i = 0, j = mid - l + 1;
  34. for (int k = l; k <= r; k++) {
  35. if (i == mid - l + 1) {
  36. nums[k] = tmp[j++];
  37. } else if (j == r - l + 1 || tmp[i] <= tmp[j]) {
  38. nums[k] = tmp[i++];
  39. } else if (tmp[i] > tmp[j]) {
  40. nums[k] = tmp[j++];
  41. }
  42. }
  43. }
  44. }