排序算法-4(javascript) 归并排序的实现

归并排序实际是使用了分治再合并的思想:

  • 分治:
    它每轮会把数组分割成2分部分,如果分割的部分还很多数,可以按照这个方法继续分割,直到分割成简单序列(比如分割到剩最后一个数了,一个数的序列自然就是最简单的有序序列)。

  • 合并:
    这时候,对分割的最小部分开始,进行两两合并成有序序列,合并的实现方法是:

    1. 创建一个左指针,指向一个分割的有序序列(比如是数组arr1, 长度为n)的初始位置0;
    2. 创建一个右指针,指向另一个分割的有序序列(数组arr2,长度为m)的初始位置0;
    3. 再创建一个m+n的空数组(res),用来存储此轮的有序结果;
    4. 比较两个指针的数,哪个小就放到res中,然后该指针右移;
    5. 重复4步骤操作,直到有一个有序序列已经全被放到到res中,另一个有序序列剩下的所有值就可以直接拼接在res后面了,这样一轮操作就结束了
    6. 然后,再对再一对分割部分重复做1-5步骤,直至所有分割部分都合并完,结束

下面通过例子来说明它的思想:
初始的无序数组:[ 8, 5, 4, 9, 6, 2, 1 ]
分割:

  1. 第1步,对半分,分成两组 [8, 5, 4, 9],[6, 2, 1],此时这两还不是有序数组
  2. 第2步,各自继续对半分,[8, 5, 4, 9]分成[8, 5]和[4, 9]; [6, 2, 1]被分割成[6, 2]和[1],发现除了[1]其它都发现还不是有序数组
  3. 第3步,各自继续对半分,[8, 5]分成[8]和[5],[4, 9]分成[4]和[9], [6, 2]分成[6]和[2],这下好了,所有的都是有序序列了[8],[5],[4],[9],[6],[2],[1]
    合并:
  4. 对各层最小部分进行合并[8],[5]合并成了有序序列[5, 8];[4],[9]合并成了有序序列[4, 9],[6]和[2]合并成了[2, 6],[1]这一步没有对象合并闲置
  5. 再向上一层合并,[5, 8]和[4, 9]合并成了[4, 5, 8, 9];[2, 6]和[1]合并成了[1, 2, 6]
  6. 再向上一层合并[4, 5, 8, 9] 和 [1, 2, 6] 合并成了 [1, 2, 4, 5, 6, 8, 9],此时,已经是个完整的有序序列了,结束

复杂度分析

  • 时间复杂度:
    1. 分割过程每次步骤是n/2,所以时间复杂度为O(logn)
    2. 合并过程是遍历一个循环,O(n)
      所以总的是O(nlogn)
  • 空间复杂度:
    要重新定义一个数组存放有结果,所以是O(n)
  • 稳定性:归并排序并不会改变相同元素的相对位置,所以是一个稳定的算法

代码实现

/**
 * @description 递归实现归并排序
 * @param arr: 初始的无序队列
 * @return res: 被排好序的队列
 */
function mergeSort(arr) {
  // 如果分割得只每剩一个值,自然已经是一个有序区间,直接返回
  if(arr.length <= 1) return arr;
  // 否则分割
  let mid = 0;
  mid = Math.floor((arr.length)/2)
  console.log('mid:', mid);
  // 左子序列
  let left = arr.slice(0, mid)
  // 递归获取左子序列的分割合并,得到最后一层的有序左子序列
  left = mergeSort(left)
  // 右子序列
  let right = arr.slice(mid)
  // 递归获取右子序列的分割合并,得到最后一层的有序右子序列
  right = mergeSort(right)
  return merge(left, right)
}

/**
 * @description 合并算法
 * @param left: 要被合并的有序数组1
 * @param right: 要被合并的有序数组2
 * @return arr: 被排序好的数组
 */
function merge(left, right) {
  // 合并后存放的数组
  let res = []
  while(left.length && right.length) {
    // 如果左子序列第1个数数比较小,弹出此数放到结果队列中
    if(left[0] <= right[0]) {
      res.push(left.shift())
    // 否则,如果右子序列第1个数数比较小,弹出此数放到结果队列中
    } else {
      res.push(right.shift())
    }
  }
  // 当一个子序列排序结束时,另一个子序列就不需要再遍历比较了,直接拼接到结果后
  return res.concat(left, right)
}

// 查看所有结果
arr = [ 8, 5, 4, 9, 6, 2, 1 ]
let res = mergeSort(arr) // [1, 2, 4, 5, 6, 8, 9]
console.log("res:", res)

参考:
图灵社区:https://www.ituring.com.cn/book/miniarticle/62897
小象Web开发:https://baijiahao.baidu.com/s?id=1675262114341494342&wfr=spider&for=pc

排序算法系列文章传送门(未完,持续更新中):
排序算法-1(javascript) 冒泡、选择、插入、希尔排序的实现
排序算法-2(javascript) 快速排序的实现
排序算法-3(javascript) 堆排序的实现
排序算法-4(javascript) 归并排序的实现

你可能感兴趣的:(排序算法-4(javascript) 归并排序的实现)