图解排序算法——归并排序(javascript)

711 阅读3分钟

来自:klh2333 的 github

算法原理

归并排序是采用分治法的一个典型的应用:

分割(分)

  1. 获取数组的中点 mid,根据此点将数组一分为二
  2. 递归分割左右数组,直到数组中只剩下一个元素

合并(治)

  1. 只剩下了一个元素之后,将左右子数组合并,合并的过程中将其进行排序
  2. 两两排好序的子数组合并以后会变成大的排好序的子数组,这样递归结束后整个数组就是排好序的了。

算法图解

根据算法的原理可以知道,归并排序的重点在于 。假设有数组 [9, 4, 6, 8, 1, 3, 2, 5] ,要让其从小到大进行排列,其归并排序的思路图如下所示。

的过程中是将两两已经有序的子数组合并成一个大的有序的数组,这其中需要有一个 temp 数组用来临时存放。以上面最后一次合并为例,将 [4, 6, 8, 9] 和 [1, 2, 3, 5] 合并成 [1, 2, 3, 4, 5, 6, 8, 9] ,其具体过程如下所示,其中涉及到的英文缩写:

  • lp:lPointer,指向左子数组开头的指针
  • rp:rPointer,指向右子数组开头的指针
  • temp:临时数组
  • tp:tPointer,指向 temp 数组开头的指针

javascript 代码

代码整体的递归流程如下图所示,其中:

  • mS:对应代码中的 mergeSort
  • m:对应代码中的 merge
  • a:对应代码中的 arr
  • t:对应代码中的 t

/**
 * 归并排序
 * 输入:
 *    待排序的数组
 *    数组左边界
 *    数组右边界
 *    临时数组
 * 输出:从小到大排好序的数组
 */
function mergeSort(arr, left, right, temp) {
  // 递归进行分治
  if (left < right) {
    // 计算出中间值
    let mid = Math.floor((left + right) / 2);
    mergeSort(arr, left, mid, temp); // 左子数组
    mergeSort(arr, mid + 1, right, temp); // 右子数组
    merge(arr, left, right, temp); // 合并子数组
  }
  return arr;
}

/**
 * 合并子数组
 * 输入:
 *    待排序的数组
 *    子数组左边界
 *    子数组右边界
 *    临时数组
 * 输出:子数组排好序的数组
 */
function merge(arr, left, right, temp) {
  let mid = Math.floor((left + right) / 2);
  let lPointer = left; // 左子数组指针
  let rPointer = mid + 1; // 右子数组指针
  let tPointer = 0; // 临时数组的指针

  // 比较左右子数组,然后把较小的挑出来放到 temp
  while (lPointer <= mid && rPointer <= right) {
    if (arr[lPointer] < arr[rPointer]) {
      // 将左子数组中的值赋值到 temp 数组中
      temp[tPointer++] = arr[lPointer++];
    } else {
      // 右子数组中的值较小,将其赋值到 temp 数组中
      temp[tPointer++] = arr[rPointer++];
    }
  }

  // 左右子数组中的一个已经赋值完,那么将另一个中的剩余值依次赋值到 temp 中即可

  // 左子数组没完,右子数组完了
  while (lPointer <= mid) {
    temp[tPointer++] = arr[lPointer++];
  }

  // 左子数组完了,有子数组没完
  while (rPointer <= right) {
    temp[tPointer++] = arr[rPointer++];
  }

  // 将 temp 数组的内容赋值回原数组中
  tPointer = 0;
  for (let i = left; i <= right; i++) {
    arr[i] = temp[tPointer++];
  }
}


// 测试
let testArr = [9, 4, 6, 8, 1, 3, 2, 5];
let left = 0;
let right = testArr.length - 1;
let temp = [];
console.log(mergeSort(testArr, left, right, temp));