傳統遞歸建樹在資料量大時易爆堆疊、效能低。本文提供兩個純迴圈函式,一次遍歷即可將鄰接列表(id, pid)轉換為父節點樹與子節點樹,記憶體佔用極低、速度極快。
/**
* 建立父節點樹狀陣列
* @param array $ar 鄰接列表資料
* @param string $id 主鍵欄位名(預設 'id')
* @param string $pid 父鍵欄位名(預設 'pid')
* @return array 以 id 為鍵的多維陣列,含 parent 子陣列
*/
function find_parent($ar, $id='id', $pid='pid') {
// 1. 建立 id => row 索引
foreach ($ar as $v) $t[$v[$id]] = $v;
// 2. 遍歷掛載 parent 參考
foreach ($t as $k => $item) {
if ($item[$pid]) {
if (!isset($t[$item[$pid]]['parent'][$item[$pid]])) {
$t[$k]['parent'][$item[$pid]] = &$t[$item[$pid]];
}
}
}
return $t;
}
/**
* 建立子節點樹狀陣列
* @param array $ar 鄰接列表資料
* @param string $id 主鍵欄位名
* @param string $pid 父鍵欄位名
* @return array 以 id 為鍵的多維陣列,含 child 子陣列
*/
function find_child($ar, $id='id', $pid='pid') {
foreach ($ar as $v) $t[$v[$id]] = $v;
foreach ($t as $k => $item) {
if ($item[$pid]) {
$t[$item[$pid]]['child'][$item[$id]] = &$t[$k];
}
}
return $t;
}
$data = [
['ID'=>1, 'PARENT'=>0, 'NAME'=>'祖父'],
['ID'=>2, 'PARENT'=>1, 'NAME'=>'父親'],
['ID'=>3, 'PARENT'=>1, 'NAME'=>'叔伯'],
['ID'=>4, 'PARENT'=>2, 'NAME'=>'自己'],
['ID'=>5, 'PARENT'=>4, 'NAME'=>'兒子'],
];
$p = find_parent($data, 'ID', 'PARENT');
$c = find_child($data, 'ID', 'PARENT');
// 輸出 $c[1] 即為以「祖父」為根的完整子樹
print_r($c[1]);
Array
(
[1] => Array
(
[ID] => 1
[PARENT] => 0
[NAME] => 祖父
[child] => Array
(
[2] => Array
(
[ID] => 2
[PARENT] => 1
[NAME] => 父親
[child] => Array
(
[4] => Array
(
[ID] => 4
[PARENT] => 2
[NAME] => 自己
[child] => Array
(
[5] => Array
(
[ID] => 5
[PARENT] => 4
[NAME] => 兒子
)
)
)
)
)
[3] => Array
(
[ID] => 3
[PARENT] => 1
[NAME] => 叔伯
)
)
)
// ... 其餘節點同樣包含 child 結構
)
| 指標 | 傳統遞歸 | 本文非遞歸 |
|---|---|---|
| 時間複雜度 | O(n²) 多次遍歷 | O(n) 單次遍歷 |
| 堆疊風險 | 深層級易堆疊溢位 | 無遞歸,零風險 |
| 記憶體 | 函式呼叫堆疊佔用 | 僅一層陣列參考 |
| 程式碼量 | 約 15 行 | 各 10 行 |
function render_menu($tree, $rootId=0) {
if (!isset($tree[$rootId]['child'])) return '';
$html = '';
foreach ($tree[$rootId]['child'] as $node) {
$html .= '- ' . htmlspecialchars($node['NAME']);
$html .= render_menu($tree, $node['ID']);
$html .= '
';
}
$html .= '
';
return $html;
}
echo render_menu($c); // 直接輸出完整巢狀 - 選單
無遞歸建樹 + 遞歸渲染(僅輸出層),兼顧建構效能與輸出便利性。