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

PHP 實現無限級分類:遞歸與迭代構建樹狀結構完整指南

     編輯:PHP 綜合知識

資料結構定義

$areas = [
    ['id'=>1, 'name'=>'安徽', 'parent'=>0],
    ['id'=>2, 'name'=>'海澱', 'parent'=>7],
    ['id'=>3, 'name'=>'濉溪縣', 'parent'=>5],
    ['id'=>4, 'name'=>'昌平', 'parent'=>7],
    ['id'=>5, 'name'=>'淮北', 'parent'=>1],
    ['id'=>6, 'name'=>'朝陽', 'parent'=>7],
    ['id'=>7, 'name'=>'北京', 'parent'=>0],
    ['id'=>8, 'name'=>'上地', 'parent'=>2],
];

特點:扁平陣列、parent 指向父節點 id、根節點 parent=0。

場景一:遞歸構建子孫樹 (顯示完整層級)

<?php
/**
 * 遞歸查找所有子孫節點
 * @param array $data 扁平資料
 * @param int $parent_id 父節點 ID
 * @param int $level 當前層級 (從 1 開始)
 * @return array 樹狀結構,含 level 欄位
 */
function build_subtree($data, $parent_id = 0, $level = 1) {
    $tree = [];
    foreach ($data as $node) {
        if ($node['parent'] == $parent_id) {
            $node['level'] = $level;
            $children = build_subtree($data, $node['id'], $level + 1);
            if ($children) {
                $node['children'] = $children;
            }
            $tree[] = $node;
        }
    }
    return $tree;
}

$tree = build_subtree($areas);

// 輸出展示
function print_tree($tree, $indent = '') {
    foreach ($tree as $node) {
        echo $indent . $node['name'] . " (ID:{$node['id']}, Level:{$node['level']})<br>";
        if (!empty($node['children'])) {
            print_tree($node['children'], $indent . '    ');
        }
    }
}

print_tree($tree);
/* 輸出:
安徽 (ID:1, Level:1)
    淮北 (ID:5, Level:2)
        濉溪縣 (ID:3, Level:3)
北京 (ID:7, Level:1)
    海澱 (ID:2, Level:2)
        上地 (ID:8, Level:3)
    昌平 (ID:4, Level:2)
    朝陽 (ID:6, Level:2)
*/
?>

場景二:遞歸構建祖先路徑 (麵包屑導航)

<?php
/**
 * 遞歸查找從根到指定節點的路徑
 * @return array 由根至目標的節點陣列
 */
function build_ancestors_recursive($data, $target_id) {
    $path = [];
    foreach ($data as $node) {
        if ($node['id'] == $target_id) {
            if ($node['parent'] > 0) {
                $path = array_merge($path, build_ancestors_recursive($data, $node['parent']));
            }
            $path[] = $node;
            break;
        }
    }
    return $path;
}

$breadcrumb = build_ancestors_recursive($areas, 8); // 上地
// 結果: 北京 → 海澱 → 上地
foreach ($breadcrumb as $node) {
    echo "> {$node['name']} ";
}
?>

場景三:迭代構建祖先路徑 (效能更優)

<?php
/**
 * 迭代查找祖先路徑 (無遞歸呼叫開銷)
 */
function build_ancestors_iterative($data, $target_id) {
    $path = [];
    $current_id = $target_id;
    
    // 建立 ID -> 節點映射,O(1) 查找
    $map = [];
    foreach ($data as $node) {
        $map[$node['id']] = $node;
    }
    
    while ($current_id !== 0 && isset($map[$current_id])) {
        $node = $map[$current_id];
        $path[] = $node;
        $current_id = $node['parent'];
    }
    
    return array_reverse($path); // 根在前
}

$breadcrumb = build_ancestors_iterative($areas, 8);
// 同樣得到: 北京 → 海澱 → 上地
?>

效能比較與選擇建議

方法時間複雜度空間複雜度適用場景
遞歸子孫樹O(n²) 最壞 (每層遍歷全陣列)O(h) 呼叫堆疊資料量 < 1000、需完整樹結構輸出
遞歸祖先路徑O(n×h) h=樹高O(h)單次查詢、資料量小
迭代祖先路徑O(n + h) (預建 Map)O(n) Map推薦:高頻查詢、大資料量
資料庫遞歸 CTESQL 層面完成-MySQL 8.0+ / PostgreSQL / SQL Server

資料庫層面優化 (MySQL 8.0+ CTE)

-- 查詢節點 8 的所有祖先
WITH RECURSIVE ancestors AS (
    SELECT id, name, parent, 1 AS level
    FROM areas WHERE id = 8
    UNION ALL
    SELECT a.id, a.name, a.parent, anc.level + 1
    FROM areas a
    JOIN ancestors anc ON a.id = anc.parent
)
SELECT * FROM ancestors ORDER BY level;

前端樹形元件資料格式轉換

<?php
// 轉換為 zTree / Element UI Tree / Ant Design Tree 所需格式
function to_tree_select($tree) {
    $result = [];
    foreach ($tree as $node) {
        $item = [
            'id' => $node['id'],
            'label' => $node['name'],
            'value' => $node['id'],
        ];
        if (!empty($node['children'])) {
            $item['children'] = to_tree_select($node['children']);
        }
        $result[] = $item;
    }
    return $result;
}

$select_data = to_tree_select($tree);
// 輸出 JSON 給前端
echo json_encode($select_data, JSON_UNESCAPED_UNICODE);
?>

資料維護建議

  • 新增節點:驗證 parent_id 存在、無循環引用
  • 移動節點:遞歸更新所有子孫 parent 路徑 (或僅更新父 ID,路徑由應用層計算)
  • 刪除節點:級聯刪除子孫、或設為根節點、或禁止刪除非葉子節點
  • 大量資料:考慮 Closure Table (關係閉包表) 或 Nested Set (嵌套集合) 模型
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved