来源:https://blog.phpha.com/backup/archives/1683.html

归并(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
6
7
8
9

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/**
* 将两个有序数组合并成一个有序数组
* @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 .然后按照上面的合并数组即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

/**
* 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,因此会浪费一些资源。因此在实际的排序中还是 相对的较少使用。

举例详解PHP归并排序的实现相关推荐

  1. java web几百万分页_举例详解用Java实现web分页功能的方法

    举例详解用Java实现web分页功能的方法 发布于 2020-11-25| 复制链接 摘记: 分页问题是一个非常普遍的问题,开发者几乎都会遇到,这里不讨论具体如何分页,说明一下Web方式下分页的原理. ...

  2. 数据库事务隔离级别举例详解

    目录 一.前言 1.1.4种事务隔离级别 1.2.3种读现象 二.举例说明 2.1.读未提交 2.2.读已提交 2.3.可重复读 2.4.串行化 一.前言 本文主要对4种事务隔离级别,具体举例说明各自 ...

  3. 数据结构排序系列详解之七 归并排序

    在前面说的那几种排序都是将一组记录按关键字大小排成一个有序的序列,而归并排序的思想是:基于合并,将两个或两个以上有序表合并成一个新的有序表 归并排序算法:假设初始序列含有n个记录,首先将这n个记录看成 ...

  4. php 归并排序,详解PHP归并排序的实现

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

  5. java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  6. 数据结构--队列、双端队列实际举例详解(Python代码)

    https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注! 欢迎关注微信公众号:宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行! 如有转载,请 ...

  7. java泛型实例化_java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  8. ipv6的127位掩码如何表示_计算机子网掩码知识举例详解

    子网掩码是网络工程中一个很重要的知识点,什么是子网掩码呢,很多对网络了解不深的朋友都对子网掩码有些困惑, 不了解它有什么用?其实子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分. ...

  9. curl 使用举例详解(二)

    原文标题为:Manual – curl usage explained 九.详细信息 不同的网络协议会提供获取特定文件或者文档详细信息不用的方法.想让curl显示单个文件的详细信息,你应该使用参数-I ...

最新文章

  1. 技术图文:双指针在链表问题中的应用
  2. floyd算法_最短路径的算法:Floyd算法
  3. Spring Boot怎么样处理静态资源(静态资源映射规则)_Web开发
  4. 0315互联网新闻 | 小红书测试短视频产品“hey”;华为与故宫共同打造“5G智慧故宫”...
  5. maven中添加servlet、jsp依赖
  6. 微信扫描二维码和浏览器扫描二维码 ios和Android 分别进入不用的提示页面
  7. PHP第十次实验总结,The Clean Architecture in PHP 读书笔记(十)
  8. HDU3068 最长回文【manacher算法】
  9. /usr/include/X11/Shell.h:51:26: 致命错误:X11/SM/SMlib.h:没有那个文件或目录
  10. 计算机网络医院拓扑图方案设计,【方案】某医院计算机网络综合布线系统设计...
  11. 自建服务器解网络锁,掌握iPhone1-4代刷机技巧
  12. 虚拟机使用本地服务器配置,虚拟机搭建本地云服务器配置
  13. 手把手教你电脑下载b站视频
  14. 第十一章:项目风险管理 - (11.5 规划风险应对)
  15. 【web前端】20.手机端网页禁止长按图片保存图片
  16. Win10提示“为了对电脑进行保护,已经阻止此应用”怎么处理?
  17. 高性能平台设计——美团旅行结算平台实践
  18. 《A brief review of image denoising algorithms and beyond》
  19. win10搜索服务器文件慢,Win10系统怎么加快文件搜索速度
  20. 矿物质饲料的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告

热门文章

  1. nginx反向代理和rewrite进行解决跨域问题、去掉url中的一部分字符串,通过nginx正则生成新的url
  2. 1高并发服务器:多路IO之select
  3. 检索数据_22_根据数据项的值排序
  4. Python数据库字段拆分数据
  5. linux 创建wifi 热点_Linux创建无线WIFI热点 2.4g/5g
  6. Qt学习笔记之样式表
  7. 笔记:基于DCNN的图像语义分割综述
  8. 深度学习框架caffe及py-faster-rcnn详细配置安装过程
  9. 架构漫谈(1):什么是架构
  10. Terraform入门 - 4. destroy 基础设施