數據結構與算法之PHP排序算法(歸並排序)


一、基本思想
歸並排序算法是將兩個(或兩個以上)有序表合並成一個新的有序表,即把待排序序列分為若干個子序列,使每個子序列有序,再將已有序的子序列合並,得到完全有序的序列。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。
 
二、算法過程
歸並主要做兩件事:
1)“分解”——將序列每次折半划分。
2)“合並”——將划分后的序列段兩兩合並后排序。
 
三、算法圖解及PHP代碼實現
1、遞歸——自頂向下
遞歸過程是將待排序數組一分為二,直至排序數組就剩下一個元素為止,然后不斷的合並兩個排好序的數組
其中,合並過程如下(以第2趟歸並為例):
PHP代碼如下:
<?php
// 遞歸
function mergeSort(&$arr, $left, $right) {
    if ($left < $right) {
        // 找出中間索引
        $mid = floor(($left + $right) / 2);
        // 對左邊數組進行遞歸
        mergeSort($arr, $left, $mid);
        // 對右邊數組進行遞歸
        mergeSort($arr, $mid + 1, $right);
        // 合並
        merge($arr, $left, $mid, $right);
    }
}
 
// 將兩個有序數組合並成一個有序數組
function merge(&$arr, $left, $mid, $right) {
    $i = $left;     // 左數組的下標
    $j = $mid + 1;  // 右數組的下標
    $temp = array();// 臨時合並數組
    // 掃描第一段和第二段序列,直到有一個掃描結束
    while ($i <= $mid && $j <= $right) {
        // 判斷第一段和第二段取出的數哪個更小,將其存入合並序列,並繼續向下掃描
        if ($arr[$i] < $arr[$j]) {
            $temp[] = $arr[$i];
            $i++;
        } else {
            $temp[] = $arr[$j];
            $j++;
        }
    }
    // 比完之后,假如左數組仍有剩余,則直接全部復制到 temp 數組
    while ($i <= $mid) {
        $temp[] = $arr[$i];
        $i++;
    }
    // 比完之后,假如右數組仍有剩余,則直接全部復制到 temp 數組
    while ($j <= $right) {
        $temp[] = $arr[$j];
        $j++;
    }
    // 將合並序列復制到原始序列中
    for($k = 0; $k < count($temp); $k++) {
        $arr[$left + $k] = $temp[$k];
    }
}
 
// 測試
$arr = [85, 24, 63, 45, 17, 31, 96];
mergeSort($arr, 0, count($arr) - 1);
print_r($arr);
2、非遞歸——自底向上
非遞歸過程是將待排序數組看成n個長度為1的子序列,數組中的相鄰元素兩兩配對,將他它們排序后,構成n/2組長度為2的排序好的子數組段,然后再將他們排序成長度為4的子數組段,如此繼續下去,直至整個數組排好序。
不管遞歸還是非遞歸,其合並的邏輯都是一樣的。
 
PHP代碼如下:
<?php
//非遞歸
function mSort(&$arr) {
    $len = count($arr);
    $size = 1;
    while ($size <= $len - 1) {
        // 從第一個元素開始掃描,left 代表第一個分割的序列的第一個元素
        $left = 0;
        while ($left + $size <= $len - 1) {
            // mid 代表第一個分割的序列的最后一個元素
            $mid = $left + $size - 1;
            //right 代表第二個分割的序列的最后一個元素
            $right = $mid + $size;
            if ($right > $len - 1) { // 如果第二個序列個數不足size個
                //調整 right 為最后一個元素的下標即可
                $right = $len - 1;
            }
            // 調用歸並函數,進行分割的序列的分段排序
            merge($arr, $left, $mid, $right);
            $left = $right + 1;
        }
        $size *= 2; // 范圍擴大一倍
    }
}
 
// 將兩個有序數組合並成一個有序數組
function merge(&$arr, $left, $mid, $right) {
    $i = $left;     // 左數組的下標
    $j = $mid + 1;  // 右數組的下標
    $temp = array();// 臨時合並數組
    // 掃描第一段和第二段序列,直到有一個掃描結束
    while ($i <= $mid && $j <= $right) {
        // 判斷第一段和第二段取出的數哪個更小,將其存入合並序列,並繼續向下掃描
        if ($arr[$i] < $arr[$j]) {
            $temp[] = $arr[$i];
            $i++;
        } else {
            $temp[] = $arr[$j];
            $j++;
        }
    }
    // 比完之后,假如左數組仍有剩余,則直接全部復制到 temp 數組
    while ($i <= $mid) {
        $temp[] = $arr[$i];
        $i++;
    }
    // 比完之后,假如右數組仍有剩余,則直接全部復制到 temp 數組
    while ($j <= $right) {
        $temp[] = $arr[$j];
        $j++;
    }
    // 將合並序列復制到原始序列中
    /*for($k = 0; $k < count($temp); $k++) {
        $arr[$left + $k] = $temp[$k];
    }*/
    for ($i = $left, $k = 0; $i <= $right; $i++, $k++) {
        $arr[$i] = $temp[$k];
    }
}
 
// 測試
$arr = [85, 24, 63, 45, 17, 31, 96, 1];
mSort($arr);
print_r($arr);

 

四、效率分析

1、時間復雜度:O(nlogn)
最好情況、最壞情況和平均時間復雜度均為O(nlogn);
2、空間復雜度:O(n)
算法處理過程中,需要一個大小為 n 的臨時存儲空間保存合並序列,所以空間復雜度為O(n)。
3、穩定性:穩定
在歸並排序中,相等的元素的順序不會改變,所以它是穩定排序。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM