PHP教程:详解PHP归并排序的实现

  PHP教程:详解PHP归并排序的实现

  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表。归并排序的一个缺点是它需要存储器有另一个大小等于数据项数目的数组。如果初始数组几乎占满整个存储器,那么归并排序将不能工作,但是如果有足够的空间,归并排序会是一个很好的选择。

  假设待排序的序列:

  4 3 7 9 2 8 6

  先说思路,归并排序的中心思想是将两个已经排序好的序列,合并成一个排序的序列。

  上面的序列可以分成:

  4 3 7 9

  和

  2 8 6

  这两个序列,然后对这两个序列分别排序:结果为:

  设置为序列A,与序列B,

  3 4 7 9

  2 6 8

  将上面的两个序列 合并成一个排序好的序列:

  合并的具体思路是:

  设置两个位置指示器,分别指向序列A与序列B开始的位置:红色为指示器指向位置:

  3 4 7 9

  2 6 8

  比较两个指示器所指向的元素的值,将较小的插入到一个新的数组内,例如序列C,同时将对应的指示器向后移动一位:

  结果为:

  3 4 7 9

  2 6 8

  形成的序列C:已经被插入一个元素了,刚才较小的 元素2.

  2

  然后 再次 比较序列A与序列B中指示器所指向的元素:将小的放入到序列C中,移动相应指针,结果为:

  3 4 7 9

  2 6 8

  2 3

  以此类推,迭代执行,直到序列A或者序列B中某个指示器已经移到到数组末端。例如:

  多次比较后,序列B已经将指示器移出到序列末端(最后一个元素之后)了。

  3 4 7 9

  2 6 8

  2 3 4 6 7 8

  然后将没有用完的序列,这里面是序列A中其余的元素全部插入到序列C的后边即可,就剩下一个9 了,将其插入到序列C后即可:

  序列C结果:

  2 3 4 5 6 7 8 9

  这样就实现了将两个有序序列合并成一个有序序列的操作,

  下面先看这个合并的php代码:

  /**

  * 将两个有序数组合并成一个有序数组

  * @param $arrA,

  * @param $arrB,

  * @reutrn array合并好的数组

  */

  function mergeArray($arrA, $arrB) {

  $a_i = $b_i = 0;//设置两个起始位置标记

  $a_len = count($arrA);

  $b_len = count($arrB);

  while($a_i<$a_len && $b_i<$b_len) {

  //当数组A和数组B都没有越界时

  if($arrA[$a_i] < $arrB[$b_i]) {

  $arrC[] = $arrA[$a_i++];

  } else {

  $arrC[] = $arrB[$b_i++];

  }

  }

  //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:

  while($a_i < $a_len) {

  $arrC[] = $arrA[$a_i++];

  }

  //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:

  while($b_i < $b_len) {

  $arrC[] = $arrB[$b_i++];

  }

  return $arrC;

  }

  经过上面的分析和程序的实现,我们不难发现,合并已排序的序列的时间应该是线性的,就是说,最多会发生N-1次比较,其中N是所有元素之和。

  通过上面的描述,我们实现了将两个排序好的数组进行和并的过程。

  此时,大家可能会有疑问,这个和归并排序整个序列有什么关系?或者你是如何能够得到最开始的两个排序好的子序列的呢?

  下面,我们就来描述以下什么是归并排序,然后再看,上面的合并与归并排序的关系是如何的:

  大家不妨去想,当我们需要排序如下的数组时,我们是否可以先将数组的前半部分与数组的后半部分分别进行归并排序,然后将排序的结果合并起来呢?

  例如:待排序的数组:

  4 3 7 9 2 8 6

  先分成2部分:

  4 3 7 9

  2 8 6

  将前半部分 与 后半部分 分别看成一个序列,再次进行归并(就是拆分,排序,合并)操作

  就会变成:

  前:

  4 3

  7 9

  后:

  2 8

  6

  同样 再对每个自序列进行 归并排序,再次(拆分,排序,合并)。

  当拆分的子序列内只存在一个元素(长度为1)时,那么这个序列就不必再拆分了,就是一个排序好的数组了。然后将这个序列,与其他的序列再合并到一起即可,最终就将所有的都合并好了,成为一个完整的排序好的数组。

  程序实现:

  通过上面的描述 大家应该想到,可以使用递归程序来实现这个程序设计吧:

  想要实现这个程序,可能需要解决如下问题:

  怎么将数组做拆分:

  设定两个指示器,一个指向数组开始假定为$left,一个指向数组最后一个元素$right:

  4 3 7 9 2 8 6

  然 后判断 $left 是否小于$right,如果小于,说明这个序列内元素个数大于一个,就将其拆分成两个数组,拆分的方式是生成一个中间的指示器$center,值 为$left + $right /2 整除。结果为:3,然后将$left 到$center 分成一组,$center+1到$right分成一组:

  4 3 7 9

  2 8 6

  接下来,递归的 利用$left, $center, $center+1, $right分别做为 两个序列的 左右指示器,进行操作。知道数组内有一个元素$left==$right .然后按照上面的合并数组即可:

  /**

  * mergeSort 归并排序

  * 是开始递归函数的一个驱动函数

  * @param &$arr array 待排序的数组

  */

  function mergeSort(&$arr) {

  $len = count($arr);//求得数组长度

  mSort($arr, 0, $len-1);

  }

  /**

  * 实际实现归并排序的程序

  * @param &$arr array 需要排序的数组

  * @param $left int 子序列的左下标值

  * @param $right int 子序列的右下标值

  */

  function mSort(&$arr, $left, $right) {

  if($left < $right) {

  //说明子序列内存在多余1个的元素,那么需要拆分,分别排序,合并

  //计算拆分的位置,长度/2 去整

  $center = floor(($left+$right) / 2);

  //递归调用对左边进行再次排序:

  mSort($arr, $left, $center);

  //递归调用对右边进行再次排序

  mSort($arr, $center+1, $right);

  //合并排序结果

  mergeArray($arr, $left, $center, $right);

  }

  }

  /**

  * 将两个有序数组合并成一个有序数组

  * @param &$arr, 待排序的所有元素

  * @param $left, 排序子数组A的开始下标

  * @param $center, 排序子数组A与排序子数组B的中间下标,也就是数组A的结束下标

  * @param $right, 排序子数组B的结束下标(开始为$center+1)

  */

  function mergeArray(&$arr, $left, $center, $right) {

  //设置两个起始位置标记

  $a_i = $left;

  $b_i = $center+1;

  while($a_i<=$center && $b_i<=$right) {

  //当数组A和数组B都没有越界时

  if($arr[$a_i] < $arr[$b_i]) {

  $temp[] = $arr[$a_i++];

  } else {

  $temp[] = $arr[$b_i++];

  }

  }

  //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:

  while($a_i <= $center) {

  $temp[] = $arr[$a_i++];

  }

  //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:

  while($b_i <= $right) {

  $temp[] = $arr[$b_i++];

  }

  //将$arrC内排序好的部分,写入到$arr内:

  for($i=0, $len=count($temp); $i<$len; $i++) {

  $arr[$left+$i] = $temp[$i];

  }

  }

  //do some test:

  $arr = array(4, 7, 6, 3, 9, 5, 8);

  mergeSort($arr);

  print_r($arr);

  注意上面的代码带排序的数组都使用的是 引用传递,为了节约空间。

  而且,其中的合并数组的方式也为了节约空间做了相对的修改,把所有的操作都放到了$arr上完成,引用传递节约资源。

  好了 上面的代码就完成了归并排序,归并排序的时间复杂度为O(N*LogN) 效率还是相当客观的。

  再说,归并排序算法,中心思想是 将一个复杂问题分解成相似的小问题,再把小问题分解成更小的问题,直到分解到可以马上求解为止,然后将分解得到的结果再合并起来的一种方法。这个思想用个 成语形容叫化整为零。 放到计算机科学中有个专业属于叫分治策略(分治发)。分就是大问题变小问题,治就是小结果合并成大结果。

  分治策略是很多搞笑算法的基础,我们在讨论快速排序时,也会用到分治策略的。

  最后简单的说一下这个算法,虽然这个算法在时间复杂度上达到了O(NLogN)。但是还是会有一个小问题,就是在合并两个数组时,如果数组的总元素个数为 N,那么我们需要再开辟一个同样大小的空间来保存合并时的数据(就是mergeArray中的$temp数组),而且还需要将数据有$temp拷贝 会$arr,因此会浪费一些资源。因此在实际的排序中还是 相对的较少使用。

时间: 2024-10-15 14:27:54

PHP教程:详解PHP归并排序的实现的相关文章

使用ssh开发rest web服务支持http etag header的教程详解

原创整理不易,转载请注明出处:使用ssh开发rest web服务支持http etag header的教程详解 代码下载地址:http://www.zuidaima.com/share/1777391667989504.htm 导言 REST方式的应用程序构架在近日所产生的巨大影响突出了Web应用程序的优雅设计的重要性.现在人们开始理解"WWW架构"内在的可测量性及弹性,并且已经开始探索使用其范例的更好的方式.在本文中,我们将讨论一个Web应用开发工具--"简陋的.卑下的&q

webpack安装配置使用教程详解

webpack安装配置使用教程详解 www.111cn.net 更新:2015-09-01 编辑:swteen 来源:转载 本文章来为各位详细的介绍一下关于webpack安装配置使用教程吧,这篇文章对于不喜欢使用 jspm测试的朋友可以参考一下. webpack 入门 目录 1 安装 webpack2 初始化项目3 webpack 配置4 自动刷新5 第三方库6 模块化7 打包.构建8 webpack 模板我最近大量使用的是 jspm,但因为用它搭建的前端开发环境中,写测试代码非常困难,而项目又

navicat使用教程详解

navicat mysql数据库管理软件 用这个软件来管理mysql数据库 方便快捷,王道之选 来看一下怎么通过 navicat软件来 创建数据库和导入mysql数据库 2 3 4 5 6 7 8 9 10 11 navicat教程 navicat使用教程详解 图文版完毕

SAE上传web应用(包括使用数据库)教程详解及问题解惑

转自:http://blog.csdn.net/baiyuliang2013/article/details/24725995 SAE上传web应用(包括使用数据库)教程详解及问题解惑:       最近由于工作需求,需利用SAE平台,但在使用过程中遇到不少问题,比如如何上传应用,上传应用完毕后打不开,mysql连接不上等等,以及云豆的计费问题,结合个人使用心得,将在本帖一一解答.       1,上传web应用首先,使用SAE平台需要注册自己的账号吧,这个就不说了,然后进入我的首页,会看到: 

超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享

原创不易,转载请注明出处:超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享 代码下载地址:http://www.zuidaima.com/share/1759689106541568.htm 依赖注入,DI(Dependency Injection),它的作用自然不必多说,提及DI容器,例如spring,picoContainer,EJB容器等等,近日,google诞生了更轻巧的DI容器--Guice! 废话不多讲了,先看看Guice是如何实现

apache2.2虚拟主机配置教程详解

apache2.2虚拟主机配置教程详解 一.修改httpd.conf 打开appserv的安装目录,找到httpd.conf文件,分别去掉下面两行文字前面的#号. #LoadModule vhost_alias_module modules/mod_vhost_alias.so 去掉#意思是启用apache的虚拟主机功能. #Include conf/extra/httpd-vhosts.conf 去掉这一行的#意思是从conf/extra/httpd-vhosts.conf这个文件导入虚拟主机

重置出错?微软Win10平板Surface Pro 4重装系统教程详解

重置出错?微软Win10平板Surface Pro 4重装系统教程详解 2015-12-11 15:27:30来源:IT之家作者:凌空责编:凌空 评论:65 Surface Pro 4系统重置出错该怎么办? Surface Pro 4无法启动该怎办? Surface Pro 4平板如何重装Win10系统? 在Win10刚刚发布时,很多用户在升级Windows10后重置系统时遇到了错误.这一问题在微软自家Surface平板中也同样可能出现.今天IT之家在重置一台Surface Pro 4平板时就遇

windows上安装Anaconda和python的教程详解

一提到数字图像处理编程,可能大多数人就会想到matlab,但matlab也有自身的缺点: 1.不开源,价格贵 2.软件容量大.一般3G以上,高版本甚至达5G以上. 3.只能做研究,不易转化成软件. 因此,我们这里使用Python这个脚本语言来进行数字图像处理. 要使用Python,必须先安装python,一般是2.7版本以上,不管是在windows系统,还是Linux系统,安装都是非常简单的. 要使用python进行各种开发和科学计算,还需要安装对应的包.这和matlab非常相似,只是matla

经典Spring入门基础教程详解

经典Spring入门基础教程详解 https://pan.baidu.com/s/1c016cI#list/path=%2Fsharelink2319398594-201713320584085%2F%E7%BB%8F%E5%85%B8Spring%E5%85%A5%E9%97%A8%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%E8%AF%A6%E8%A7%A3&parentPath=%2Fsharelink2319398594-201713320584085 博达远