归并排序是成功应用分治技术的一个完美例子。
分治法的思想是:(1)将问题的实例划分为同一个问题的几个较小的实例,最好拥有同样的规模;(2)对这些较小的实例求解(一般用递归方法,但在问题规模足够小的时候。有时也会利用另一个算法);(3)如果必要的话,合并这些较小问题的解,以得到原始问题的解。
对于一个需要排序的数组$a[0...n-1],归并排序把它一分为二:$a[0..n/2-1]和$a[n/2...n-1],并对每个子数组递归排序,然后把这两个排好序的子数组合并为一个有序数组。
下面的这个图能够很好地解释归并排序的整个流程(自己画的,有点丑)。
看上面的图,我们就知道了。递归的将一个数组分成两部分,直到小的足以解决问题就不再递归(一个数,一定是有序的)。然后再将两个有序的数组合并,最后整个数组就是有序的了。
算法如下:
1 <?php 2 /** 3 * 归并排序 4 * 递归调用mergeSort来对数组$a排序 5 * @param $a 一个可排序的数组 6 * @return $a 非降序排列的数组 7 */ 8 function mergeSort(&$a){ 9 $t = count($a); 10 if($t<=1){ 11 return; 12 } 13 $b = array_slice($a, 0, intval($t/2)); 14 $c = array_slice($a, intval($t/2)); 15 16 /* echo '<br/>$b: '; 17 print_r($b); 18 echo '<br/>$c: '; 19 print_r($c); */ 20 21 mergeSort($b); 22 mergeSort($c); 23 merge($b, $c, $a); 24 25 /* echo "<br/>".'$a: '; 26 print_r($a); 27 echo "<br/>"; */ 28 } 29 /** 30 * 将两个有序数组$y, $z合并为一个有序数组$x 31 * @param $y, $z, $x 32 * @return $x 33 */ 34 function merge(&$y, &$z, &$x){ 35 $i = $j = $k = 0; 36 $p = count($y); 37 $q = count($z); 38 while($i<$p && $j<$q){ 39 if($y[$i]<$z[$j]){ 40 $x[$k] = $y[$i]; 41 $i++; 42 }else{ 43 $x[$k] = $z[$j]; 44 $j++; 45 } 46 $k++; 47 } 48 if($i==$p){ 49 while($j<$q){ 50 $x[$k] = $z[$j]; 51 $j++; 52 $k++; 53 } 54 }else{ 55 while($i<$p){ 56 $x[$k] = $y[$i]; 57 $i++; 58 $k++; 59 } 60 } 61 } 62 ?>
如果想看中间的输出结果,只需要把mergeSort()中的注释去掉就可以了。
归并排序与快排相比,归并排序是规定的排序,这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其他信息尽量按输入的顺序排列很重要,这也是它比快排有优势的地方。