Motivate
MergeSort是个相对古老的算法了,为什么现在我们还要讨论这么古老的东西呢?有几个原因:
  • 它虽然年龄很大了,但是在实践中一直被沿用,仍然是很多程序库中的标准算法之一。
  • 实现它的本质是分治思想,是一个理解分治算法思想的好例子,好起点。
  • 本文会使用“递归树”来对它进行运行时间分析,后面会集合这种思路生成“主方法”。
题目
 
输入一个数组,数组里面的每个数字是不重复的,输出是已经排序好的数组。
比如输入的是:
期望输出的是:
可能之前我们有所知道一些排序算法,比如SelectionSort,扫描全数组找到最小元素,把它放到输出数组的第一位,接着扫描复制次小的元素,以此类推;比如BubbleSort,对相邻无序的元素进行比较,执行反复的交换,直到最后数组完成排序。等等。但这些算法的问题就是运行时间是平方级的。那我们来看看今天这个排序算法用更少的运行时间是怎么实现的。
例子 想要理解MergeSort算法是如何运行的,一个最简单的方法就是看看具体的例子。
整体过程就是:
它把数组 [5, ,4, 1, 8, 7, 2, 6, 3] 划分为更小的数组(子问题)[5, 4, 1, 8] 和 [7, 2, 6, 3]排序,通过神奇的递归操作,得到每个排序后的子数组,再将子数组合并起来。
伪代码 将上面的图换成伪代码就是这样的过程
而这个Merge程序怎么实现呢?
由上面的图我们可以知道,Merge的时候,其实输入两个已经排序好的数组C, D,再把它们排序输出到B。
索引 k 操控的是输出数组,索引 i,j 操控的是已排序好的C和D子数组,都是从左向右扫描。每一次的for循环,其实就是找C和D中最小的数字,因为C和D是已排序好的数组,所以最小的数字就是i或j对应的元素。比较后把它放入输出数组B中,并将对应的索引+1,这样下次循环就跳过已复制的元素了。所以最后的数组B输出是按序方式生成的。
算法分析
我们先来对Merge程序算算它的执行操作数量。 第1,2行有一次赋值操作。 第3行是一个for循环,每一个for循环里,有比较操作(C[i]和D(i)比较),赋值操作(B[k]的赋值),递增操作(i或j加1),循环变量k还要加1,所以每一次循环一共有4次操作。
一共就是4n+2次操作,为了后面的计算方便,当n>=1时,4n+2<6n(去掉常数项), 我们取6n次操作作为Merge程序操作上界。
我们现在再来看MergeSort的运行时间。 为了简单起见,假如输入数组的长度是n的2次方(如果没有这个假设只需要额外工作就能消除这个假设),我们用递归树的方法来分析运行时间的上界,每一个节点就表示一次递归调用。
最外层是整个原始的输入数组,它在进行MergeSort的时候会有2个递归调用,所以这是一个二叉树(每个节点都有两个子节点),第一层的2个节点就是原始数组的左半部分和右半部分,再次递归最后到达最底层,一个长度为1或0的数组。我们需要知道几个数量:
原始数组长度是n,递归树有多少层?
由于每深入一层,数组长度就缩小一半,第0层是n,第1层是n/2(除了一次2),第2层是n/4(除了2次2),最后一层的数组长度是不大于1的,就是除以了log2(n)次2,所以最后一层是log2(n)层。(也可以假定n/2^a = 1, 求解a)如果没有n是2的次方这个假设,就向上取整。一共就是log2(n) +1层。
递归树的第j层有多少个节点(子问题)?每个节点的数组长度是多少?
因为每一层的递归数量是前一层的两倍,所以第j层就有2^j个子问题。每个节点的长度,总长度是n,均分到每个节点就是n/(2^j)个长度。
所以总的运行时间就是:
层数 * 每一层的工作量
= 层数 * (第 j 层的子问题数量 * 每个第j层子问题完成的工作数) = 层数 * (第 j 层的子问题数量 * (每个第j层子问题的规模 * Merge的时间))
= (log2(n)+1) * ( 2^j * n/(2^j) * 6n)
=6n * log2(n) +6n
= O(nlogx(n))
这时候,我们就可以理直气壮的说递归的分治算法比其它更简单的算法要快的多啦。看图直观感受一下
当数据非常大的时候,它是非常有优势的,指数函数增长十分的缓慢。
今日互动
有什么不明白的欢迎在评论区留言。
 

斯坦福算法分析和设计_2. 排序算法MergeSort的更多相关文章

  1. 在一个N个整数数组里面,有多个奇数和偶数,设计一个排序算法,令所有的奇数都在左边。

    //在一个N个整数数组里面,有多个奇数和偶数,设计一个排序算法,令所有的奇数都在左边. // 例如: 当输入a = {8,4,1,6,7,4,9,6,4}, // a = {1,7,9,8,4,6,4 ...

  2. java排序算法(三):堆排序

    java排序算法(三)堆排序 堆积排序(HeapSort)是指利用堆积树这种结构所设计的排序算法,可以利用数组的特点快速定位指定索引的元素.堆排序是不稳定的排序方法.辅助空间为O(1).最坏时间复杂度 ...

  3. 算法分析中最常用的几种排序算法(插入排序、希尔排序、冒泡排序、选择排序、快速排序,归并排序)C 语言版

    每次开始动手写算法,都是先把插入排序,冒泡排序写一遍,十次有九次是重复的,所以这次下定决心,将所有常规的排序算法写了一遍,以便日后熟悉. 以下代码总用一个main函数和一个自定义的CommonFunc ...

  4. Python排序算法(六)——归并排序(MERGE-SORT)

    有趣的事,Python永远不会缺席! 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/10800699.html 一.归并排序(MERG ...

  5. 简单排序算法设计(Java)

    总共有八种排序算法,还是慢慢看吧 1.简单排序算法 简单排序算法就是设置标兵,逐个比较数,然后查找插入位置,插入 public static void p(int[] a){ for(int i=0; ...

  6. 排序算法之归并排序(Mergesort)解析

    转自:http://www.cnblogs.com/ayqy/p/4050452.html   一.归并排序的优缺点(pros and cons) 耗费心思来理解它,总要有个理由吧: 归并排序的效率达 ...

  7. 十大经典排序算法总结(JavaScript描述)

    前言 读者自行尝试可以想看源码戳这,博主在github建了个库,读者可以Clone下来本地尝试.此博文配合源码体验更棒哦~~~ 个人博客:Damonare的个人博客 原文地址:十大经典算法总结 这世界 ...

  8. 转载部长一篇大作:常用排序算法之JavaScript实现

    转载部长一篇大作:常用排序算法之JavaScript实现 注:本文是转载实验室同门王部长的大作,找实习找工作在即,本文颇有用处!原文出处:http://www.cnblogs.com/ywang172 ...

  9. 常用排序算法之JavaScript实现

    1.插入排序 1)算法简介 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从 后向前扫描,找到相应位置并插入 ...

随机推荐

  1. Python--day28--面试题

  2. JVM基础--JVM参数之堆栈空间配置

    目录 堆配置 年轻代 Eden区 永久代(JDK1.7) 元空间(JDK1.8) 栈空间 直接内存 总结 参考资料 JVM系列目录 JVM 中最重要的一部分就是堆空间了,基本上大多数的线上 JVM 问 ...

  3. H3C 基本ACL部署位置示例

  4. 使用原生JS封装一个动画函数

    最近一直在忙项目,很少有时间回顾之前的知识,今天刚好要做一个轮播,因为对兼容性有一定的要求,使用了各种插件和库中的轮播,效果都不是很理想,一怒之下,使用原生JS封装了一个轮播组件,其中重要的功能就是一 ...

  5. Python--day36--操作系统的作用;多道技术;

  6. 扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)

    传送门 •题目描述 题目描述 众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只. 同时,扶桑号战列舰 ...

  7. Server,Servlet,ServletConfig,ServletContext,Session,Request,Response

    Server流程 解析URL->找到应用->找到Servlet->实例化Servlet->调用init->调用service->返回响应->调用destroy ...

  8. 5款顶尖Windows文件传输工具

    5款顶尖Windows文件传输工具 英文原文: Drasko 日常工作中,公司里的系统管理员或其他岗位的员工都需要传递大量各种类型的文件和文档.其中一些可以通过 email 收发.但由于 email ...

  9. linux ioctl 系统调用预定义的命令

    尽管 ioctl 系统调用最常用来作用于设备, 内核能识别几个命令. 注意这些命令, 当用 到你的设备时, 在你自己的文件操作被调用之前被解码. 因此, 如果你选择相同的号给一 个你的 ioctl 命 ...

  10. 读《Effect Java中文版》

    读<Effect Java中文版> 译者序 序 前言 第1章引言 1   第2章创建和销毁对象 4 第1条:考虑用静态工厂方法代替构造函数 4 第2条:使用私有构造函数强化singleto ...