归并排序之入门到"放弃"

前面我们已经讲解过了冒泡排序、选择排序、插入排序、 希尔排序。本次我们将讲解归并排序,希望大家学习完之后,能够徒手快速手写一个归并排序的实现代码。

由来

归并排序首先由著名的现代计算机之父 John_von_Neumann (冯·诺依曼)在 1945 年发明,被用在了 EDVAC(一台美国早期电子计算机),足足用墨水写了 23 页的排序程序。

归并排序是一种分治策略的排序算法。它是一种比较特殊的排序算法,通过递归地先使每个子序列有序,再将两个有序的序列进行合并成一个有序的序列。

核心思想

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

总体来说就是: “分”与“合”

主体流程

分而治之

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

合并相邻有序子序列

再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

具体的步骤可以总结为:

  • 先申请一个辅助数组,长度等于两个有序数组长度的和。
  • 从两个有序数组的第一位开始,比较两个元素,哪个数组的元素更小,那么该元素添加进辅助数组,然后该数组的元素变更为下一位,继续重复这个操作,直至数组没有元素。
  • 返回辅助数组。

上述过程已经很详细了,但这里我们还是采用文字详细的描述一下相关过程:

有序数组A:[4, 5, 7, 8]有序数组B:[1, 2, 3, 6][] 表示比较的范围。

因为 1 有序数组A:[4, 5, 7, 8]有序数组B:1 [2, 3, 6] 辅助数组:1

因为 2 有序数组A:[4, 5, 7, 8]有序数组B:1 2 [3, 6]辅助数组:1 2

因为 3 有序数组A:[4, 5, 7, 8]有序数组B:1 2 3 [6]辅助数组:1 2 3

因为 4 有序数组A:4 [5, 7, 8]有序数组B:1 2 3 [6]辅助数组:1 2 3 4

因为 5 有序数组A:4 5 [7, 8]有序数组B:1 2 3 [6]辅助数组:1 2 3 4 5

因为 6 有序数组A:4 5 [7, 8]有序数组B:1 2 3 6 []辅助数组:1 2 3 4 5 6

因为数组B已经没有元素了,所以整个A右边序列移动过去,最终排序完成为:1 2 3 4 5 6 7 8

实现

按照上面的过程或者思路,我们可以写出主体伪代码:

# 归并排序中的思路MergeSort(arr[], l,  r)If r > l     1. 找到数组中的中间点,把数组分为两部分             middle m = (l+r)/2     2. 对数组的左部分调用MergeSort 函数               mergeSort(arr, l, m)     3. 对数组的右部分调用MergeSort 函数              mergeSort(arr, m+1, r)     4. 合并2,3中的两部分             merge(arr, l, m, r)

结合伪代码和上述的图解实例,我们可以写出具体的代码实现,下述是我实现的代码:

package sort

func MergeSort(array []int) { arrayLen := len(array) if arrayLen 1 {  panic("array len is less than 1") } start := 0 end := arrayLen - 1 mergeSort(array, start, end)}

func mergeSort(array []int, start int, end int) { if start >= end {  return } //找到数组中的中间点,把数组分为两部分 mid := (start + end) / 2 //对数组的左部分调用mergeSort 函数 mergeSort(array, start, mid) //对数组的右部分调用nergeSort 函数 mergeSort(array, mid+1, end) //合并数组的左右部分 merge(array, start, mid, end) return}

func merge(array []int, start int, mid int, end int) { // 申请一个辅助数组来合并两个有序数组,这两个数组是 array[start,mid],array[mid+1,end) tempArr := make([]int, 0, end-start+1) // 左数组移标 i := start // 右数组移标 j := mid + 1

 k := 0 // 两个数组开始比较 for ; i <= mid && j <= end; k++ {  if array[i] <= array[j] {   tempArr = append(tempArr, array[i])   i++  } else {   tempArr = append(tempArr, array[j])   j++  } }

 //剩余数组拼接 for ; i <= mid; i++ {  tempArr = append(tempArr, array[i]) }

 for ; j <= end; j++ {  tempArr = append(tempArr, array[j]) }

 // 将辅助数组的元素复制回原数组 for i, v := range tempArr {  array[start+i] = v } return}

时间复杂度

最优时间复杂度:O(n*log(n))

最坏时间复杂度:O(n*log(n))

平均时间复杂度:O(n*log(n))

空间复杂度

归并的空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间:n + logn;所以空间复杂度为: O(n)

稳定性

在分解的子列中,有1个或2个元素时,1个元素不会交换,2个元素如果大小相等也不会交换。在序列合并的过程中,如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,所以,归并排序也是稳定的

优化

这里就不展开详细的算法优化了,不过可以提供几种常见的思路就是:

  • 当递归到规模足够小时,利用插入排序
  • 归并前判断一下是否还有必要归并
  • 只在排序前开辟一次空间

总结

归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。它在时间上是非常有效的(最差时间复杂度和最优时间复杂度都为 O(nlogn)  ),但是这种算法很消耗空间,一般来说在内部排序不会用这种方法,而是用快速排序;外部排序才会考虑到使用这种方法;

参阅

图解排序算法(四)之归并排序

快速排序伪代码_归并排序之入门到quot;放弃quot;相关推荐

  1. 快速排序伪代码_数据结构和算法之快速排序

    快速排序是一种有意思的排序算法.下面我们从核心思想,图解,递推公式,优化和性能度量五个方面讲讲快速排序. 快排的核心思想是这样的 -对于排序数组中下标为p......r之间的一组数据,我们选择p到r的 ...

  2. dt测试软件的学习心得,无线网络优化dt测试心得_适合新手入门,高手进阶_5年项目经验实战经验.docx...

    无线网络优化DT测试心得_适合新手入门,高手进阶_5年项目经验实战经验 无线网络优化DT测试心得_适合新手入门,高手进阶_5年项目经验实战经验 路测中不常见的问题和个人心得 1.深井子镇投诉测试报告 ...

  3. go语言视频教程_ go语言入门视频教程_go语言实战视频教程

    许多人可能知道go语言的优点在哪里,但他们不知道go语言适合在哪里使用.通过学习go语言视频教程,可以清楚的知道go语言主要用于服务器端开发,其定位是用来开发"大型软件".学习go ...

  4. 学习笔记:Java 并发编程①_基础知识入门

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 视频下载: ...

  5. 数据结构和算法_零基础入门01

    数据结构和算法_零基础入门01 一.数据结构是什么? 逻辑结构.物理结构 二.算法 算法的五个基本特征 算法设计的要求 b站学习小甲鱼的数据结构与算法,自留笔记. 程序设计=数据结构+算法 一.数据结 ...

  6. 从入门到不放弃:多浏览器的自动化测试(1)- 本地测试

    本文将作为多浏览器自动化测试的第一篇文章,给读者从头介绍如何进行本地多浏览的自动化测试工作,包括测试的原理.测试框架的选取.测试工程的搭建和实现等.另外"从入门到不放弃"系列将给读 ...

  7. 快速排序比较次数_归并排序「从入门到放弃」

    归并排序 归并排序,是创建在归并操作上的一种有效的排序算法,效率为O(nlogn).1945年由约翰·冯·诺伊曼首次提出.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用 ...

  8. 归并排序比较次数_归并排序「从入门到放弃」

    归并排序 归并排序,是创建在归并操作上的一种有效的排序算法,效率为O(nlogn).1945年由约翰·冯·诺伊曼首次提出.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用 ...

  9. 排序中减治法算法伪代码_算法浅谈——分治算法与归并、快速排序(附代码和动图演示)...

    在之前的文章当中,我们通过海盗分金币问题详细讲解了递归方法. 我们可以认为在递归的过程当中,我们通过函数自己调用自己,将大问题转化成了小问题,因此简化了编码以及建模.今天这篇文章呢,就正式和大家聊一聊 ...

最新文章

  1. ActiveMQ的多节点集群
  2. python编程 语言-Python现在还是最火的编程语言吗?
  3. HTTP 代理如何正确处理 Cookie
  4. 2020-10-19 Keil安装及使用
  5. 一个最简单的WebSocket hello world demo
  6. html 循环_一个不被程序员认为是编程语言的语言——HTML,你怎么看?
  7. 当我们谈论 996 的时候我们在谈论什么?
  8. 采用install.sh安装脚本进行快速安装astercc
  9. Python 之操作so动态库
  10. word在试图打开文件时遇到错误,检查稳定或驱动器文件权限
  11. Java测试用例简介
  12. myeclipse创建web-project没有WebRoot文件夹
  13. 密码学的安全性浅析3
  14. 安装MySQL绿色版本,不用装软件、不用装软件、不用装软件
  15. 平板电脑:apple、中国挑大梁
  16. 淘宝店群月入万元,店群20大常见方法都在这
  17. 如何高效进行出货复核作业,提升出货准确率?
  18. nas 群晖 git 项目创建步骤
  19. asp之小旋风服务器
  20. 新年了,5G手机芯片,到底买谁?

热门文章

  1. [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门
  2. 关于xinetd报错
  3. 用java打出矩形阵型的数字_java输出数字发散矩形
  4. 信息学奥赛一本通 2069:【例2.12】糖果游戏
  5. OpenJudge NOI 1.5 25:求特殊自然数
  6. 数列分段(信息学奥赛一本通-T1428)
  7. 和为k的倍数(51Nod-2522)
  8. 搜索 —— 启发式搜索 —— A* 算法
  9. 数据结构 —— 线段树
  10. 信息学奥赛C++语言: 蛇形方阵1