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

(0)

相关推荐

  • C++归并排序算法实例

    归并排序 归并排序算法是采用分治法的一个非常典型的应用.归并排序的思想是将一个数组中的数都分成单个的:对于单独的一个数,它肯定是有序的,然后,我们将这些有序的单个数在合并起来,组成一个有序的数列.这就是归并排序的思想.它的时间复杂度为O(N*logN). 代码实现 复制代码 代码如下: #include <iostream> using namespace std;   //将有二个有序数列a[first...mid]和a[mid...last]合并. void mergearray(int

  • C++实现自顶向下的归并排序算法

    本文实例讲述了C++实现自顶向下的归并排序算法.分享给大家供大家参考,具体如下: 一. 算法描述 自顶向下的归并排序:采用分治法进行自顶向下的程序设计方式,分治法的核心思想就是分解.求解.合并. 1. 先将长度为N的无序序列分割平均分割为两段 2. 然后分别对前半段进行归并排序.后半段进行归并排序 3. 最后再将排序好的前半段和后半段归并 过程(2)中进行递归求解,最终下图详细的分解了自顶向下的合并算法的实现过程: 二. 算法实现 /*==============================

  • C语言实现排序算法之归并排序详解

    排序算法中的归并排序(Merge Sort)是利用"归并"技术来进行排序.归并是指将若干个已排序的子文件合并成一个有序的文件. 一.实现原理: 1.算法基本思路 设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中. (1)合并过程 合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置.合并时依次比较R[i

  • 归并排序的原理及java代码实现

    概述 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序采用的是递归来实现,属于"分而治之",将目标数组从中间一分为二,之后分别对这两个数组进行排序,排序完毕之后再将排好序的两个数组"归并"到一起,归并排序最重要的也就是这个"归并"的过程,归并的过程中需要额外的跟需要归并的两个数组长度一致的空间. 效果图: 步骤 申请空间,

  • java二路归并排序示例分享

    归并排序就是采用分治法进行排序: (1)将一个数组分成小的2个数组分别进行排序: (2)之后将分出来的已经拍好序的数组进行合并: 复制代码 代码如下: import java.util.Scanner;public class MergeSort {    int[] a=null;    int[] b=null;    int n;    Scanner sin=null; MergeSort()    {        a=new int[10000];        b=new int[

  • php 归并排序 数组交集

    复制代码 代码如下: $a=array('1','2','3','4','22'); $b=array('1','3','4','11','22','23'); f($a, $b, 5, 6, $t); print_r($t); function f(&$a, &$b, $n, $m, &$t){ $i=0;$j=0; while($i<$n && $j<$m){ if($a[$i]==$b[$j]){ echo $a[$i]." "

  • 详解PHP归并排序的实现

    归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表.归并排序的一个缺点是它需要存储器有另一个大小等于数据项数目的数组.如果初始数组几乎占满整个存储器,那么归并排序将不能工作,但是如果有足够的空间,归并排序会是一个很好的选择. 假设待排序的序列: 4 3 7 9 2 8 6 先说思路,归并排序的中心思想是将两个已经排序好的序列,合并成一个排序的序列. 上面的序列可以分成: 4 3 7 9 和 2  8  6 这两个序列,然后对这两个序列分别排序:结果为: 设置为序列A,与序列

  • PHP排序算法之归并排序(Merging Sort)实例详解

    本文实例讲述了PHP排序算法之归并排序(Merging Sort).分享给大家供大家参考,具体如下: 基本思想: 归并排序:就是利用归并(合并)的思想实现的排序方法.它的原理是假设初始序列含有 n 个元素,则可以看成是 n 个有序的子序列,每个子序列的长度为 1,然后两两归并,得到 ⌈ n / 2⌉ (⌈ x ⌉ 表示不小于 x 的最小整数)个长度为 2 或 1 的有序序列:再两两归并,······,如此重复,直至得到一个长度为 n 的有序序列为止,这种排序方法就成为 2 路归并排序. 一.归并

  • PHP排序算法之基数排序(Radix Sort)实例详解

    本文实例讲述了PHP排序算法之基数排序(Radix Sort).分享给大家供大家参考,具体如下: 基数排序在<大话数据结构>中并未讲到,但是为了凑齐八大排序算法,我自己通过网络学习了这个排序算法,并给大家分享出来. 基本思想: 基数排序(radix sort)属于"分配式排序"(distribution sort),又称"桶子法"(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些"桶&quo

  • java 算法之归并排序详解及实现代码

    java 算法之归并排序详解 一.思想 归并排序:将一个数组排序,可以先(递归地)将它分成两半部份分别排序,然后将结果归并起来: 二.概念 归并:将两个有序的数组归并成一个更大的有序数组: 三.特点 优点:能够保证将任意长度为N的数组排序所需要的时间和NlogN成正比: 缺点:需要额外的空间和N成正比: 四.实现方法 将两个不同的有序数组归并到第三个数组中: 先将前半部分排序,在将后半部分排序,然后在数组中移动元素而不需要使用额外的空间: 五.代码 /** * 归并排序 * * @author

  • java 归并排序的实例详解

    java 归并排序的实例详解 归并排序 归并排序,指的是将两个已经排序的序列合并成一个序列的操作. 归并操作的过程如下: 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 设定两个指针,最初位置分别为两个已经排序序列的起始位置 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 重复步骤3直到某一指针到达序列尾 将另一序列剩下的所有元素直接复制到合并序列尾 Java代码 /** * 归并排序 * * @param ts */ @SuppressWa

  • java 中归并排序算法详解

    java 中归并排序算法详解 归并排序算法,顾名思义,是一种先分再合的算法,其算法思想是将要排序的数组分解为单个的元素,每个元素就是一个单个的个体,然后将相邻的两个元素进行从小到大或从大到小的顺序排序组成一个整体,每个整体包含一到两个元素,然后对相邻的整体继续"合"并,因为每个整体都是排过序的,因而可以采用一定的算法对其进行合并,合并之后每个整体包含三到四个元素,继续对相邻的整体进行合并,直到所有的整体都合并为一个整体,最终得到的整体就是将原数组进行排序之后的结果. 对于相邻的整体,其

  • C语言数据结构 链表与归并排序实例详解

    C语言数据结构 链表与归并排序实例详解 归并排序适合于对链表进行原址排序,即只改变指针的连接方式,不交换链表结点的内容. 归并排序的基本思想是分治法:先把一个链表分割成只有一个节点的链表,然后按照一定顺序.自底向上合并相邻的两个链表. 只要保证各种大小的子链表是有序的,那么最后返回的链表就一定是有序的. 归并排序分为分割和合并两个子过程.分割是用递归的方法,把链表对半分割成两个子链表:合并是在递归返回(回朔)的时候,把两个有序链表合并成一个有序链表. (注意:只有一个节点的链表一定是有序的) 这

  • 数据结构之归并排序的实例详解

    归并排序 基本思想         归并排序是建立在二路归并和分治法的基础上的一个高效排序算法,将已有序的子序列合并,得到完全有序的序列:即先使每个子序 列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为二路归并. 将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表:将这些有序序列 再次归并,得到n/4个长度为4的有序序列:如此反复进行下去,最后得到一个长度为n的有序序列.所以呢,我们总结一下归并排序 其实就只有两步:

  • Java 归并排序算法、堆排序算法实例详解

    基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序示例: 合并方法: 设r[i-n]由两个有序子表r[i-m]和r[m+1-n]组成,两个子表长度分别为n-i +1.n-m. j=m+1:k=i:i=i; //置两个子表的起始下标及辅助数组的起始下标 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束 //选取r[i]和r[j]较小的存入辅助数组

  • C++ 归并排序(merge sort)案例详解

    核心思想:"分"与"合". 主体流程 先将一个序列分成很多个不能再分割的子序列,将各个子序列分别排序后再将子序列合并.其实就是重复两个步骤:[1]分[2]合并. 首先是第一个小问题,怎么分? 比如说一个序列:12 ,23,1,44,233,10,9,8.我们先分成两段:12 ,23,1,44 和 233,10,9,8, 发现还能再分成4段:12 ,23 和 1,44------233,10 和 9,8. 再分成8段:12--23--1--44 和233--10--9

随机推荐