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

PHP 利用引用賦值實現無限級分類樹:極簡代碼構建遞歸結構

     編輯:PHP 相關知識

PHP 利用引用賦值實現無限級分類:告別遞歸查詢,單次遍歷構建樹形結構

無限級分類是電商、CMS、OA 等系統的核心模型。傳統做法要么遞歸查詢數據庫(N+1 問題),要么在 PHP 中遞歸構建樹(代碼冗長)。本文介紹一種基於 引用賦值(&) 的單次遍歷算法:將扁平數組轉為巢狀樹結構,代碼不足 30 行,性能極高,邏輯清晰。

數據模型

標準 id / pid / name 結構:

idpidname
10四川
20重慶
31成都
41綿陽
53高新區

核心算法實現

<?php
/**
 * 扁平數組轉無限級分類樹(引用賦值版)
 * @param array $orig 原始數組,每項需含 id、pid 字段
 * @return array 樹形結構數組
 */
public static function get_tree($orig) {
    // 1. 重建索引:以 id 為鍵,便於 O(1) 查找父節點
    $items = array();
    foreach ($orig as $key => $value) {
        $items[$value['id']] = $value;
    }

    // 2. 單次遍歷組裝樹
    $tree = array();
    foreach ($items as $key => $item) {
        if ($item['pid'] == 0) {
            // 頂級分類:直接掛入樹根,使用引用賦值
            $tree[] = &$items[$key];
        } else {
            // 非頂級分類
            if (isset($items[$item['pid']])) {
                // 父節點存在:將當前節點以引用方式加入父節點的 child 數組
                $items[$item['pid']]['child'][] = &$items[$key];
            } else {
                // 父節點不存在(數據異常或非第一層):作為孤兒節點掛入樹根
                $tree[] = &$items[$key];
            }
        }
    }
    return $tree;
}

原理解析:為什麼只需一次遍歷?

  1. 索引映射:第一步將數組鍵名統一為 id,使 $items[$pid] 能直接定位父節點,無需遞歸查找。
  2. 引用賦值(&):$items[$pid]['child'][] = &$items[$key] 不是複製數據,而是將子節點的 內存地址 存入父節點。因此,當後續處理到該子節點時,它已經掛在父節點下;同理,子節點的子節點也會自動掛在正確位置。
  3. 順序無關:因為引用指向同一內存塊,數組順序不影響最終樹形。即使子節點在父節點前出現,父節點處理時引用已存在,關係自動建立。

輸出結果示例

Array
(
    [0] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 四川
            [child] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [pid] => 1
                            [name] => 成都
                            [child] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 5
                                            [pid] => 3
                                            [name] => 高新區
                                        )
                                )
                        )
                    [1] => Array
                        (
                            [id] => 4
                            [pid] => 1
                            [name] => 綿陽
                        )
                )
        )
    [1] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 重慶
        )
)

優化與避坑指南

  1. 字段名兼容:實際項目中字段可能為 category_id/parent_id,建議增加參數映射或預處理統一鍵名。
  2. 循環引用檢測:若數據存在環(A 的父是 B,B 的父是 A),會導致無限遞歸。可在遍歷時維護 $visited 集合檢測並拋出異常。
  3. 大數據分頁:單表超 1 萬行時,建議數據庫層面用 path 欄位(如 0/1/3/5)存儲路徑,查詢時 LIKE '0/1/%' 獲取子樹,再由 PHP 組裝。
  4. 返回值去引用:若需 json_encode 輸出,PHP 5.4+ 自動處理引用;舊版本可用 json_encode($tree, JSON_UNESCAPED_UNICODE) 或先 unset($tree) 斷開引用。
  5. 類靜態方法:建議封裝為 TreeUtil::build($list, 'id', 'pid', 'child') 通用工具類,提高復用性。

性能對比

方案時間複雜度空間複雜度代碼行數適用場景
遞歸查詢 DBO(N) 查詢次數O(1)~10極小數據、實時性要求極高
PHP 遞歸構建O(N²) 最壞O(N)~20中小數據、邏輯簡單
引用賦值單遍歷O(N)O(N)~25大數據、高性能、通用推薦
Closure Table / PathO(log N) 查詢O(N log N)DB 結構變更超大規模、頻繁移動節點

掌握「引用賦值 + 索引映射」這一模式,不僅能優雅解決無限級分類,同樣適用於部門組織架構、評論回復巢狀、菜單權限樹等所有「扁平轉樹形」場景。建議收藏入庫,下次遇到樹形結構直接套用。

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