美文网首页
排序:归并排序

排序:归并排序

作者: 菲利普马洛 | 来源:发表于2019-01-06 18:20 被阅读8次

原理

  1. 拆分:将一个数组拆分成两个数组,左数组和右数组。然后声明一个空的新数组。
  2. 合并:比较两个数组最前面的元素,把小的移动到新数组里面。注意,移动这个元素之后,该数组里面已经没有该元素了。
  3. 重复第二步,直到左右数组其中一个为空。
  4. 将左右数组中不为空那一个剩余的元素放到新数组里面。

注意,归并排序是递归调用的。拆分时持续拆分数组,直到左右数组只包含一个元素。然后将这些数组进行合并操作。

例子

归并排序

代码

// 归并排序

/**
 * 归并排序中合并两个数组,并返回结果
 * @param {Array} left
 * @param {Array} right
 * @returns {Array}
 */
const merge = (left, right) => {
  let result = []
  let i = 0
  let j = 0

  while (i < left.length && j < right.length) {

    if (left[i] > right[j]) {
      result.push(right[j++])
    }
    else {
      result.push(left[i++])
    }
  }

  while (i < left.length) {
    result.push(left[i++])
  }

  while (j < right.length) {
    result.push(right[j++])
  }

  return result
}

const mergeSort = (list) => {

  if (list.length < 2) {
    return list
  }

  let middle = Math.floor(list.length / 2)
  let left = list.slice(0, middle)
  let right = list.slice(middle)

  return merge(mergeSort(left), mergeSort(right))
}

代码中,以 ij 对左右数组的遍历来代表移动元素的动作,被移动到新数组的元素实际上还存在于数组中,只是不被遍历了。而上面原理部分说成移动只是为了更好地表述。

延伸

假如写成移动元素的话,即从左右数组中删除原来的元素,推测涉及到删除操作,开销也就更大,代码的性能就会受到影响。简单测试了下,的确是遍历的方式比较快。

// 归并排序(涉及删除数组元素)

/**
 * 归并排序中合并两个数组,并返回结果
 * @param {Array} left
 * @param {Array} right
 * @returns {Array}
 */
const merge2 = (left, right) => {
  let result = []

  while (left.length && right.length) {

    if (left[0] > right[0]) {
      result.push(right.shift())
    }
    else {
      result.push(left.shift())
    }
  }

  while (left.length) {
    result.push(left.shift())
  }

  while (right.length) {
    result.push(right.shift())
  }

  return result
}

const mergeSort2 = (list) => {

  if (list.length < 2) {
    return list
  }

  let middle = Math.floor(list.length / 2)
  let left = list.slice(0, middle)
  let right = list.slice(middle)

  return merge2(mergeSort2(left), mergeSort2(right))
}
/**
 * 生成指定长度与指定最大值的随机数组
 * @param {Number} length 数组长度,默认 50
 * @param {Number} maxValue 数组最大值,默认 100
 * @returns {Array}
 */
const genRandomArray = (length = 50, maxValue = 100) => {
  let arr = []

  for (let i = 0; i < length; i++) {
    arr[i] = Math.ceil(Math.random() * maxValue)
  }

  return arr
}

/**
 * 测试排序一个数组所花费的时间
 * @param {Function} sort 排序算法函数
 * @param {Array} list 待排序数组
 */
const timeTest = (sort, list, sortName) => {
  console.time(sortName)
  sort(list)
  console.timeEnd(sortName)
}

// 测试函数
const test = () => {

  for (let i = 0; i< 10; i++) {
    let list = genRandomArray(100)
    timeTest(mergeSort, list, 'mergeSort')
    timeTest(mergeSort2, list, 'mergeSort2')
    console.log('\n')
  }
}

运行结果如下:


可见,涉及到删除元素的归并排序,虽然写起来代码简洁,可运行起来性能不如遍历形式的。

参考文献

[1] 饥人谷算法教程:选择排序--效率测试
[2] Liang ,Y.Daniel. Java 语言程序设计:进阶篇[M]. 李娜,译. 机械工业出版社,2011:73-76.

相关文章

  • 排序算法

    约定 选择排序 冒泡排序 插入排序 希尔排序 归并排序1. 归并方法2. 自顶向下归并排序3. 自底向上归并排序 ...

  • 排序二:归并、快排

    文章结构 归并排序 快速排序 源码 1. 归并排序 1.1 什么是归并排序 归并排序的思想是:将待排序的区间平分成...

  • 算法 第二章第二部分笔记

    各种排序算法的性能特点 选择排序 插入排序 希尔排序 归并排序 本地归并排序 自底向上的归并排序 快速排序 三向切...

  • 常见的排序算法(2)

    要点 快速排序 归并排序 1.快速排序 2.归并排序

  • Datawhale | 编程第6期 Test 3

    排序 1.实现归并排序、快速排序、插入排序、冒泡排序、选择排序、堆排序(选做) 归并排序 快速排序 插入排序 冒泡...

  • php-归并排序、快速排序、堆排序

    归并排序、快速排序、堆排序 时间复杂度 O(nlogn) 归并排序 快速排序(快排) 堆排序

  • 排序 -- 快排/归并

    聊聊排序吧 冒泡排序 选择排序 插入排序 快速排序 归并排序 计数排序 桶排序 堆排序 本篇 快排/归并 之前的三...

  • java归并排序

    归并排序什么是归并排序:图解归并排序归并排序有两种实现方式,一是基于递归,而是基于迭代1)基于递归的归并排序: 基...

  • 6-十大排序篇二

    十大排序(2) 今天先学习第二大类排序算法 归并排序 排序排序 希尔排序 堆排序 1.归并排序 分析:利用归并的思...

  • 排序方法

    1.选择排序 2.插入排序 3.冒泡排序 归并排序归并排序5.快速排序快速排序

网友评论

      本文标题:排序:归并排序

      本文链接:https://www.haomeiwen.com/subject/fdasrqtx.html