php实现归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

<?php
function mergearray($a,$b){
    $a_point = 0;//可以理解为指向a的第一个位置
    $b_point = 0;
    $a_length = count($a);//a的长度
    $b_length = count($b);
    $tmp = array();//一个临时数组来合并这两个数组
    $t_point = 0;
    while($a_point < $a_length && $b_point < $b_length){
        if($a[$a_point] < $b[$b_point]){
            $tmp[$t_point++] = $a[$a_point++];
        }else{
            $tmp[$t_point++] = $b[$b_point++];
        }
    }

    //哪一个有序列还有剩余,直接追加到tmp数组后面
    while($a_point < $a_length){
        $tmp[$t_point++] = $a[$a_point++];
    }

    while($b_point < $b_length){
        $tmp[$t_point++] = $b[$b_point++];
    }
    return $tmp;
}
$a = array(12,36,43,44,59,62);
$b = array(7,35,52,85,94);
$merge = mergearray($a,$b);
var_dump($merge)
?>

再换一个问题,如果有序列b追加到有序列a的后面,变成只有一个序列$a = array(12,36,43,44,59,62,7,35,52,85,94); 那怎么进行归并排序?
我们仍可以用下标来将它分成两个有序列,第一个有序列从下标$a[0]$a[5],第二个有序列从$a[6]$a[10]
所以我们需要找到分成这两个有序列的中点的下边标,即下标5或者是下标6.
先不急,先假设这个中点下标是已知的:

<?php
function mergearray($a,$first,$mid,$last){
    $a_point = $first;//指向有序列的第一个下标
    $a_end = $mid;//该有序列的终点下标
    $b_point = $mid+1;
    $b_end = $last;
    $tmp = array();//一个临时数组来合并这两个数组
    $t_point = 0;
    while($a_point <= $a_end && $b_point <= $b_end){//注意判断条件的等于号
        if($a[$a_point] < $a[$b_point]){
            $tmp[$t_point++] = $a[$a_point++];
        }else{
            $tmp[$t_point++] = $a[$b_point++];
        }
    }

    while($a_point <= $a_end){
        $tmp[$t_point++] = $a[$a_point++];
    }

    while($b_point <= $b_end){
        $tmp[$t_point++] = $a[$b_point++];
    }
    return $tmp;
}
$a = array(12,36,43,44,59,62,7,35,52,85,94);
$merge = mergearray($a,0,5,count($a)-1);
var_dump($merge)
?>
?>

到目前为止,所做的操作只是归并中的并操作(合并),那什么归操作
归操作就是递归地把一个序列分成两组,再将每一组再分成两组,依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序
php实现:

<?php
function mergearray(&$arr,&$tmp,$first,$mid,$last){
    $first_start = $first;
    $first_end = $mid;
    $last_start = $mid+1;
    $last_end = $last;
    $t = $first;
    while($first_start<=$first_end&&$last_start<=$last_end){
        if($arr[$first_start] < $arr[$last_start]){
            $tmp[$t++] = $arr[$first_start++];
        }else{
            $tmp[$t++] = $arr[$last_start++];
        }
    }

    while($first_start<=$first_end){
        $tmp[$t++] = $arr[$first_start++];
    }

    while($last_start <= $last_end){
        $tmp[$t++] = $arr[$last_start++];
    }
    for($i = $first; $i <= $last; $i++){
        $arr[$i] = $tmp[$i];
    }
}
function mergesort(&$arr,&$tmp,$first,$last){
    if($first < $last){
        $mid = (int)(($first+$last)/2);
        mergesort($arr,$tmp,$first,$mid);
        mergesort($arr,$tmp,$mid+1,$last);
        mergearray($arr,$tmp,$first,$mid,$last);
    }
}

$arr = array(44,12,59,36,62,43,94,7,35,52,85);
$tmp = array();
mergesort($arr,$tmp,0,count($arr)-1);
unset($tmp);//释放tmp
var_dump($arr);

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。


参考资料:
白话经典算法系列之五 归并排序的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值