首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 經典排序算法全集:冒泡、歸並、快速、選擇、插入、二分查找完整實現與複雜度分析

     編輯:PHP 綜合知識

前言

排序算法是程序員必備基本功,PHP 雖內置 sort()、usort() 等高效函數,但理解底層原理對優化業務邏輯、面試、編寫擴展至關重要。本文匯總 7 種經典算法的 PHP 實現,包含時間/空間複雜度分析、穩定性說明、適用場景與優化建議。

算法複雜度總覽

算法平均時間最好情況最壞情況空間複雜度穩定性備註
冒泡排序O(n²)O(n)O(n²)O(1)穩定教學用,實戰極少用
選擇排序O(n²)O(n²)O(n²)O(1)不穩定交換次數少
插入排序O(n²)O(n)O(n²)O(1)穩定小數組/近乎有序極快
快速排序O(n log n)O(n log n)O(n²)O(log n)不穩定實戰首選,PHP 內部採用
歸並排序O(n log n)O(n log n)O(n log n)O(n)穩定外部排序、鏈表排序首選
二分查找O(log n)O(1)O(log n)O(1)/O(log n)-需有序數組

1. 冒泡排序

/**
 * 冒泡排序:相鄰元素兩兩比較,大的後移
 * @param array $arr 引用傳遞排序
 * @return void
 */
function bubble_sort(array &$arr): void {
    $n = count($arr);
    for ($i = 0; $i < $n - 1; $i++) {
        $swapped = false;
        // 優化:每輪將最大值冒泡到末尾,下輪減少比較次數
        for ($j = 0; $j < $n - 1 - $i; $j++) {
            if ($arr[$j] > $arr[$j + 1]) {
                [$arr[$j], $arr[$j + 1]] = [$arr[$j + 1], $arr[$j]]; // PHP 7.1+ 解構賦值交換
                $swapped = true;
            }
        }
        if (!$swapped) break; // 本輪無交換,已有序
    }
}

// 測試
$arr = [64, 34, 25, 12, 22, 11, 90];
bubble_sort($arr);
print_r($arr); // [11, 12, 22, 25, 34, 64, 90]

關鍵優化

  • 標志位提前終止:若某輪無交換,說明已有序
  • 雙向冒泡(雞尾酒排序):奇數輪正向,偶數輪反向,減少移動距離

2. 歸並排序

/**
 * 歸並排序:分治法,遞歸分解再合併
 * @param array $arr
 * @return array 新排序數組(不修改原數組)
 */
function merge_sort(array $arr): array {
    $len = count($arr);
    if ($len <= 1) return $arr;
    
    $mid = $len >> 1; // 位運算除以 2
    $left = merge_sort(array_slice($arr, 0, $mid));
    $right = merge_sort(array_slice($arr, $mid));
    
    return merge($left, $right);
}

/**
 * 合併兩個有序數組
 */
function merge(array $left, array $right): array {
    $result = [];
    $i = $j = 0;
    $lLen = count($left);
    $rLen = count($right);
    
    while ($i < $lLen && $j < $rLen) {
        $result[] = ($left[$i] <= $right[$j]) ? $left[$i++] : $right[$j++];
    }
    // 追加剩餘
    while ($i < $lLen) $result[] = $left[$i++];
    while ($j < $rLen) $result[] = $right[$j++];
    
    return $result;
}

// 測試
$arr = [38, 27, 43, 3, 9, 82, 10];
print_r(merge_sort($arr)); // [3, 9, 10, 27, 38, 43, 82]

工程化改進

  • 原地歸並:減少 array_slice 複製開銷,傳遞索引範圍
  • 小數組切換插入排序:閾值(如 16)以下用插入排序
  • 迭代版本:避免遞歸棧溢出,自底向上合併

3. 快速排序

/**
 * 快速排序:選取基準,分區,遞歸
 * @param array $arr
 * @return array
 */
function quick_sort(array $arr): array {
    $n = count($arr);
    if ($n <= 1) return $arr;
    
    $pivot = $arr[0]; // 簡單選取首元素,生產建議三數取中或隨機
    $left = $right = [];
    
    for ($i = 1; $i < $n; $i++) {
        if ($arr[$i] <= $pivot) {
            $left[] = $arr[$i];
        } else {
            $right[] = $arr[$i];
        }
    }
    
    return array_merge(quick_sort($left), [$pivot], quick_sort($right));
}

// 三數取中優化版
function quick_sort_optimized(array $arr): array {
    $n = count($arr);
    if ($n <= 1) return $arr;
    if ($n <= 16) return insertion_sort($arr); // 小數組用插入排序
    
    // 三數取中選基準
    $mid = $n >> 1;
    $pivot = median_of_three($arr[0], $arr[$mid], $arr[$n - 1]);
    
    $left = $right = $equal = [];
    foreach ($arr as $v) {
        if ($v < $pivot) $left[] = $v;
        elseif ($v > $pivot) $right[] = $v;
        else $equal[] = $v;
    }
    
    return array_merge(quick_sort_optimized($left), $equal, quick_sort_optimized($right));
}

function median_of_three($a, $b, $c) {
    if (($a - $b) * ($c - $a) >= 0) return $a;
    return (($b - $a) * ($c - $b) >= 0) ? $b : $c;
}

function insertion_sort(array $arr): array {
    for ($i = 1, $n = count($arr); $i < $n; $i++) {
        $key = $arr[$i];
        $j = $i - 1;
        while ($j >= 0 && $arr[$j] > $key) {
            $arr[$j + 1] = $arr[$j];
            $j--;
        }
        $arr[$j + 1] = $key;
    }
    return $arr;
}

PHP 內部實現參考

PHP 7+ 的 zend_qsort 採用三路快速排序(荷蘭國旗問題),針對大量重複鍵優化,並對小分區切換插入排序,非遞歸實現避免棧溢出。

4. 選擇排序

/**
 * 選擇排序:每輪選最小值放到已排序區末尾
 * @param array $arr
 * @return array
 */
function selection_sort(array $arr): array {
    $n = count($arr);
    for ($i = 0; $i < $n - 1; $i++) {
        $minIdx = $i;
        for ($j = $i + 1; $j < $n; $j++) {
            if ($arr[$j] < $arr[$minIdx]) $minIdx = $j;
        }
        if ($minIdx !== $i) {
            [$arr[$i], $arr[$minIdx]] = [$arr[$minIdx], $arr[$i]];
        }
    }
    return $arr;
}

5. 插入排序

/**
 * 插入排序:將當前元素插入已排序區適當位置
 * @param array $arr
 * @return array
 */
function insertion_sort(array $arr): array {
    $n = count($arr);
    for ($i = 1; $i < $n; $i++) {
        $key = $arr[$i];
        $j = $i - 1;
        // 向右移動大於 key 的元素
        while ($j >= 0 && $arr[$j] > $key) {
            $arr[$j + 1] = $arr[$j];
            $j--;
        }
        $arr[$j + 1] = $key;
    }
    return $arr;
}

應用場景

  • 數組長度 < 50 時比快速排序更快
  • 近乎有序數組(時間複雜度接近 O(n))
  • 在線排序(數據流式到達)

6. 二分查找(遞歸版 & 迭代版)

/**
 * 二分查找遞歸版
 * @param array $arr 有序數組
 * @param int $low 低索引
 * @param int $high 高索引
 * @param mixed $target 目標值
 * @return int|false 索引或 false
 */
function binary_search_recursive(array $arr, int $low, int $high, $target) {
    if ($low > $high) return false;
    
    $mid = (($low + $high) >> 1); // 位運算平均,防溢出
    if ($arr[$mid] === $target) return $mid;
    
    return $target < $arr[$mid] 
        ? binary_search_recursive($arr, $low, $mid - 1, $target)
        : binary_search_recursive($arr, $mid + 1, $high, $target);
}

/**
 * 二分查找迭代版(推薦生產環境,無遞歸開銷)
 */
function binary_search_iterative(array $arr, $target) {
    $low = 0;
    $high = count($arr) - 1;
    
    while ($low <= $high) {
        $mid = (($low + $high) >> 1);
        if ($arr[$mid] === $target) return $mid;
        if ($target < $arr[$mid]) {
            $high = $mid - 1;
        } else {
            $low = $mid + 1;
        }
    }
    return false;
}

// 測試
$arr = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91];
var_dump(binary_search_iterative($arr, 23)); // int(5)
var_dump(binary_search_recursive($arr, 0, 9, 23)); // int(5)

7. 實戰建議:PHP 內置函數 vs 手寫算法

場景推薦方案理由
一般業務排序sort()/rsort()/usort()C 級實現、高度優化、支持自定義比較
關聯數組按值排序asort()/arsort()保持鍵值關係
關聯數組按鍵排序ksort()/krsort()保持鍵值關係
多字段複雜排序array_multisort()支持多維度、混合排序
學習/面試/特殊約束手寫算法理解原理、應對特殊需求(如外部排序、穩定性要求)
海量數據外部排序歸並排序 + 文件分塊內存受限、磁順序讀寫

總結

掌握經典排序算法的核心在於理解分治思想(快速/歸並)、增量構建(插入/選擇)、雙指針/縮小區間(二分)。PHP 開發中 99% 場景直接使用內置函數,但理解算法邊界(穩定性、空間換時間、小數組優化)能幫助你在性能調優、異常排查、系統設計中做出正確決策。建議將本文代碼封裝為 Algorithm\Sort 命名空間庫,配合 PHPUnit 單元測試,作為團隊技術資產沉澱。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved