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

PHP 無限級分類遞歸排序:樹形結構扁平化輸出、巢狀集合模型與非遞歸替代方案

     編輯:PHP 綜合知識

將扁平的分類陣列 (含 id, pid) 按層級順序排序輸出 (父節點在前、子節點緊跟),是選單渲染、麵包屑、分類管理常見需求。本文提供標準遞歸函數 order(),分析其呼叫堆疊深度風險,並介紹非遞歸迭代法、資料庫層面解決方案 (遞歸 CTE、巢狀集合、閉包表) 及效能對比。

標準遞歸實作

1, 'pid'=>0, 'name'=>'電子產品'],
    ['id'=>2, 'pid'=>1, 'name'=>'手機'],
    ['id'=>3, 'pid'=>2, 'name'=>'蘋果'],
    ['id'=>4, 'pid'=>2, 'name'=>'安卓'],
    ['id'=>5, 'pid'=>1, 'name'=>'筆電'],
    ['id'=>6, 'pid'=>0, 'name'=>'家電'],
];

$sorted = order($cats);
/*
輸出順序:
1 電子產品 (pid=0)
2 手機 (pid=1)
3 蘋果 (pid=2)
4 安卓 (pid=2)
5 筆電 (pid=1)
6 家電 (pid=0)
*/

遞歸深度風險與優化

⚠️ 風險:PHP 預設最大遞歸深度 ~100-200 (受 xdebug.max_nesting_level、記憶體限制)。分類超過 100 層 (罕見) 會導致 Maximum function nesting level exceeded。

優化 1:預建索引,避免每次遍歷全陣列

function orderOptimized(array $data, int $pid = 0): array {
    // 建立 pid => 子節點列表 索引 O(N)
    $children = [];
    foreach ($data as $item) {
        $children[$item['pid']][] = $item;
    }

    $result = [];
    // 使用棧模擬遞歸 (深度優先)
    $stack = [[$pid, 0]]; // [當前pid, 子節點索引]
    $iterators = [];     // 每層的迭代器狀態

    while ($stack) {
        [$currentPid, $index] = array_pop($stack);
        $kids = $children[$currentPid] ?? [];

        if ($index < count($kids)) {
            $item = $kids[$index];
            $result[] = $item;
            // 將下一個同層節點壓回棧
            $stack[] = [$currentPid, $index + 1];
            // 將第一個子節點壓入棧 (深度優先)
            $stack[] = [$item['id'], 0];
        }
    }
    return $result;
}

優化 2:生成器 逐行輸出 (記憶體友善)

function orderGenerator(array $children, int $pid = 0): Generator {
    foreach ($children[$pid] ?? [] as $item) {
        yield $item;
        yield from orderGenerator($children, $item['id']);
    }
}

// 使用
$children = []; foreach ($data as $d) $children[$d['pid']][] = $d;
foreach (orderGenerator($children) as $item) {
    echo $item['name'] . "\n"; // 逐行處理,不佔用結果陣列記憶體
}

資料庫層面解決方案 (大數據量首選)

模型原理查詢子樹移動節點適用場景
鄰接表 (Adjacency List)pid 欄位遞歸 CTE / PHP 遞歸簡單讀多寫少、層級淺
路徑枚舉 (Path Enumeration)path: '1/2/3/'LIKE '1/2/%'更新路徑讀多、需快速祖先/後代
巢狀集合 (Nested Set)lft, rgt 編號lft BETWEEN ...複雜 (重新編號)極少變動、大量讀取
閉包表 (Closure Table)單獨表存所有祖先後代關係JOIN closure插入/刪除關係行讀寫平衡、需遞歸聚合

MySQL 8.0+ 遞歸 CTE 查詢完整樹 (推薦現代專案)

WITH RECURSIVE category_tree AS (
    -- 錨點:頂層分類
    SELECT id, pid, name, 0 AS depth, CAST(name AS CHAR(1000)) AS path
    FROM categories WHERE pid = 0

    UNION ALL

    -- 遞歸:子分類
    SELECT c.id, c.pid, c.name, ct.depth + 1, CONCAT(ct.path, ' > ', c.name)
    FROM categories c
    INNER JOIN category_tree ct ON c.pid = ct.id
)
SELECT * FROM category_tree ORDER BY path;

前端渲染建議

  • 後端輸出**樹狀巢狀結構** (而非扁平排序),前端遞歸組件渲染 <ul><li>。
  • 樹狀構建:$tree = []; foreach($data as $d) $tree[$d['pid']][] = $d; function build($pid){ foreach($tree[$pid]??[] as $c){ $c['children']=build($c['id']); $res[]=$c; } return $res??[]; }
  • 大樹分頁/虛擬滾動:只載入可視區節點,懶加載子節點。

PHP 端遞歸排序適合中小型分類 (千筆內、層級 < 10)。大型系統請下推至資料庫 (遞歸 CTE) 或採用閉包表/巢狀集合,配合前端樹狀組件實現高效能分類管理。

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