資料庫中儲存扁平化的分類資料(如省市區、商品分類),每筆記錄包含 id、parent_id、name。前端需以樹狀結構顯示(縮排層級)。本文示範一種不需遞迴、純陣列運算的排序演算法。
$array = [
0 => ['', '河北'], // 頂層,parent 為空
1 => ['', '北京'],
2 => [0, '保定'], // parent_id = 0 (河北)
3 => [1, '海澱'], // parent_id = 1 (北京)
4 => [3, '中關村'], // parent_id = 3 (海澱)
5 => [2, '涿州'], // parent_id = 2 (保定)
];
typeArray()<?php
function typeArray($array) {
$pathMap = []; // key => '祖先路徑字串'
// 1. 建立每個節點的完整祖先路徑(以 | 分隔)
foreach ($array as $k => $v) {
$parentKey = $v[0];
if (is_numeric($parentKey)) {
// 子節點:繼承父節點路徑 + 自己 key
$pathMap[$k] = ($pathMap[$parentKey] ?? '') . $k . '|';
} else {
// 頂層節點:路徑為自己 key
$pathMap[$k] = $k . '|';
}
}
// 2. 依路徑字串排序(字典序即可保證父節點在前、同層按 key 排序)
asort($pathMap);
// 3. 輸出:依路徑中 | 的數量決定縮排層級
$output = '';
foreach ($pathMap as $k => $path) {
$level = substr_count($path, '|') - 1; // 減 1 因結尾多一個 |
$indent = str_repeat('-', $level);
$output .= $indent . $array[$k][1] . "\n";
}
return $output;
}
echo typeArray($array);
/* 輸出:
河北
-保定
--涿州
北京
-海澱
--中關村
*/
?>
"0|2|5|",父節點路徑必為子節點路徑的前綴。asort() 依路徑字串排序,天然保證「父先於子、同層按 key 順序」。substr_count($path, '|') - 1 即為深度(根節點深度 0)。| 優點 | 缺點 |
|---|---|
| 無遞迴,不會爆堆疊 | 需先載入全部資料到記憶體 |
| 單次遍歷 + 排序,時間複雜度 O(n log n) | 不支援動態新增/刪除節點即時更新 |
| 程式碼極簡,易維護 | 依賴 key 為數字索引,若為關聯陣列需調整 |
<?php
function buildTree($flat, $parentKey = 0) {
$tree = [];
foreach ($flat as $k => $v) {
if ($v[0] == $parentKey || ($parentKey === 0 && $v[0] === '')) {
$children = buildTree($flat, $k);
$node = ['id' => $k, 'name' => $v[1]];
if ($children) $node['children'] = $children;
$tree[] = $node;
}
}
return $tree;
}
$tree = buildTree($array);
echo json_encode($tree, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
?>
id (PK)、parent_id (INDEX)、name、sort_orderSELECT id, parent_id, name, sort_order FROM categories ORDER BY sort_order 撈回全表typeArray() 或 buildTree() 組裝樹狀結構利用「路徑字串編碼 + 字典序排序」即可將扁平分類資料高效轉為樹狀輸出,無需遞迴查詢資料庫,適合中小型分類系統。若需 API 回傳 JSON,再以遞迴函式組裝巢狀陣列即可。