归并排序

归并排序

  1. 把 2 个及以上的有序子序列,"归并" 成一个有序序列
  2. 内部排序中,多采用 二路 归并排序
  3. 整个归并排序只需要 logN 躺
  • 基本思想:

    1. 切分步骤:先将大数组,拆分成 2 组,然后 2 组再拆分,直到元素个数为 1,
    2. 合并步骤:接着两个 单一元素比较合并,合并完再合并 两个 二元素,接着 两个 四元素
  • 具体思路:

    1. 递归切分数组
    2. 若当前数组 数量小于 1,无需排序,直接返回结果
    3. 否则当前数组分成 2 个子数组,递归排序这 2 个子数组
    4. 在子数组排序结束后,将子数组的结果归并成排好序的数组
  • 关键问题:

    1. 如何将两个有序序列,合并成一个?
      • 分别用指针指向两个不同的有序序列,比较当前指针所在的元素,哪个小,移出哪个,且移出所在指针前进一位重复比较
      • 若 一方 已为空,则直接把另一方有序序列,全部接在后面
  • 时间复杂度: O(n logn)

  • 空间复杂度: O(n),因为需要一个与原始序列同样大小的辅助序列

let list = [8, 4, 5, 7, 1, 3, 6, 2];

let temp = [];

function MergeSort(list, start, end) {
  if (end - start < 1) return; //为了细化分到单个元素,所以不需要 =

  // 分 (大化小)
  let mid = Math.floor((start + end) / 2);
  MergeSort(list, start, mid);
  MergeSort(list, mid + 1, end);

  // 治 (从单个元素开始合并成有序)
  Merge(list, start, mid, end, temp);
}

MergeSort(list, 0, list.length - 1);
console.log(list);

function Merge(list, start, mid, end, temp) {
  let l_start = start;
  let r_start = mid + 1;
  let i = 0;

  // mid实则为 l_end, end实则为r_end
  while (l_start <= mid && r_start <= end) {
    //比较 左右两边,将其按顺序塞进 temp数组里
    if (list[l_start] < list[r_start]) {
      temp[i++] = list[l_start++];
    } else {
      temp[i++] = list[r_start++];
    }
  }

  //如果 一边完了,另一边还有的话,直接全部塞进 temp里
  while (l_start <= mid) {
    temp[i++] = list[l_start++];
  }

  while (r_start <= end) {
    temp[i++] = list[r_start++];
  }

  //temp已经是有序了,这里利用temp将list对应的下标调整为有序
  //这样做了,才可以让 下一次递归是建于 list有序 的基础之上
  i = 0;
  while (start <= end) {
    list[start++] = temp[i++];
  }
}