PHP常用演算法補充

weixin_34041003發表於2017-03-16

五、歸併排序 O(n * log n)。
其實歸併排序是一種拆分,合併的思想。和快速排序思想有共通之處,左邊一堆,右邊一堆,然後進行合併。通過遞迴實現排序。 區別之處呢? 他們的區別也是思想上本質的區別,快速排序的拆分,是選擇了特定的值進行大小比較,從而分為left 和 right 。也就是小的一堆放入left,大的一堆放入right。而後,小的left 再細分為left1 right1。。。。通過進行類似的遞迴完成排序。也就是說,一直細分下去,遞迴最末尾的left1就是最小值。

而歸併排序,是從幾何上的左右切分,一直遞迴切分成2或者1的最小粒度的陣列,然後才開始進行比較大小,然後合併。此處的比較大小是:兒子left的元素 和兒子的right元素 進行比較,而後進行排序合併成為父親left或者right。在此,直到拿到各自排序合併完成最後兩個陣列:最起初的left 和right,也僅僅直到他們各自的順序,並不能確認整個陣列的順序,還是需要通過最終的left right 比較後合併才能完成真正意義上的排序。
<?php
function gbSort($arr){
if(count($arr)<=1){return $arr;}
$min = floor(count($arr)/2);//取中間數字進行拆分
$left = array_slice($arr,0,$min);
$right = array_slice($arr,$min);
$left = gbSort($left); //遞迴
$right = gbSort($right);
return get_merge($left,$right);//呼叫排序合併函式進行合併
}
function get_merge($left,$right){
while(count($left) && count($right)){
$m[] = $left[0]>$right[0] ? array_shift($right) : array_shift($left);
//進行比較,小的移除,並且放入到陣列$m中。
}
return array_merge($m,$left,$right);//進行合併(由於不知道left right 哪個會先為空,所以進行統一合併)
}
?>

六、堆排序
本例中fixDown函式實現對某一個節點的向下調整,這裡預設的是起始節點為1,方便計算父子節點關係

起始節點為1的父子關係: 父節點k, 子節點為2K、2k+1 子節點j, 父節點為 floor(j/2) floor為向下取整
起始節點為0的父子關係: 父節點k, 子節點為2K+1, 2k+2 子節點j, 父節點為 floor((j-1)/2)
引數$k為調整點位置, $lenth為陣列長度,也就是從1起始到最後一個節點的座標.

<?php
function fixDown(&$arr, $k, $lenth)
{
  while(2$k<=$lenth) { //只要當前節點有子節點, 就需要繼續該迴圈
$j = $k
2;
if ($j<$lenth && $arr[$j]<$arr[$j+1]) $j++; // 只要子節點有右節點,且右節點比左節點大,那麼切換到右節點操作。
if ($arr[$j] < $arr[$k]) break; // 如果子節點都沒有父節點大, 那麼調整結束。
exch($arr[$j], $arr[$k]);
    $k = $j;
}
}

function exch(&$a, &$b) {
$tmp = $a; $a = $b; $b = $tmp;
}

function headSort(&$arr)
{
$len = count($arr);
array_unshift($arr, NULL);
for($i=$len/2;$i>=1;$i--) {
fixDown($arr, $i, $len);
}
while($len>1) {
exch($arr[1], $arr[$len]);
fixDown($arr, 1, --$len);
}
array_shift($arr);
}
$arr = array(4,6,4,9,2,3);
headSort($arr);
?>

相關文章