PHP实现从子节点向上遍历父节点生成逗号分隔分类字符串
问题:从嵌套分类JSON中根据子节点ID生成向上遍历的路径字符串
你有一个abc.json文件,结构如下:
{ "meta": { "code": 200, "requestId": "5afd9043dd57977f0f88d3b1" }, "response": { "categories": [ { "id": "1", "categories": [ { "id": "a", "categories": [] }, { "id": "b", "categories": [ { "id": "c", "categories": [] }, { "id": "d", "categories": [ { "id": "e", "categories": [] }, { "id": "f", "categories": [] } ] } ] } ] }, { "id": "2", "categories": [ { "id": "i", "categories": [] }, { "id": "j", "categories": [ { "id": "k", "categories": [] }, { "id": "l", "categories": [] } ] } ] } ] } }
需求是:指定子节点ID时,生成从根到该节点的路径字符串(比如指定c得到'1,b,c',指定i得到'2,i')。你尝试的递归代码没有达到预期效果,下面我来帮你实现正确的方案。
解决方案1:递归直接查找路径
这个方法适合单次查找的场景,递归遍历分类树的同时记录当前路径,找到目标节点时直接返回完整路径。
完整代码示例
// 读取并解析JSON文件 $jsonContent = file_get_contents('abc.json'); $data = json_decode($jsonContent, true); $rootCategories = $data['response']['categories']; /** * 递归查找目标节点的路径 * @param array $categories 当前遍历的分类列表 * @param string $targetId 要查找的节点ID * @param array $currentPath 当前已遍历的路径 * @return string|null 找到的路径字符串,未找到则返回null */ function findCategoryPath($categories, $targetId, $currentPath = []) { foreach ($categories as $category) { // 将当前节点ID加入临时路径 $tempPath = $currentPath; $tempPath[] = $category['id']; // 找到目标节点,返回拼接后的路径 if ($category['id'] === $targetId) { return implode(',', $tempPath); } // 如果当前节点有子分类,递归查找 if (!empty($category['categories'])) { $result = findCategoryPath($category['categories'], $targetId, $tempPath); // 子节点找到结果,直接返回 if ($result !== null) { return $result; } } } // 遍历完未找到目标,返回null return null; } // 测试调用 echo findCategoryPath($rootCategories, 'c'); // 输出:1,b,c echo "\n"; echo findCategoryPath($rootCategories, 'i'); // 输出:2,i
代码说明
- 函数会遍历每个分类节点,把当前节点ID加入临时路径;
- 如果当前节点就是目标ID,直接将路径数组转为逗号分隔的字符串返回;
- 如果当前节点有子分类,就递归传入子分类、目标ID和当前临时路径;
- 一旦递归找到结果,就立即返回,避免不必要的遍历。
解决方案2:构建父节点映射表(适合多次查找)
如果需要多次查询不同节点的路径,先构建一个「节点ID => 父节点ID」的映射表,之后从目标节点向上追溯父节点,最后反转路径得到结果,效率更高。
完整代码示例
// 读取并解析JSON文件 $jsonContent = file_get_contents('abc.json'); $data = json_decode($jsonContent, true); $rootCategories = $data['response']['categories']; /** * 构建节点与父节点的映射表 * @param array $categories 当前遍历的分类列表 * @param string|null $parentId 当前分类的父节点ID * @param array &$map 存储映射关系的数组(引用传递) */ function buildParentMap($categories, $parentId = null, &$map = []) { foreach ($categories as $category) { $map[$category['id']] = $parentId; // 递归处理子分类 if (!empty($category['categories'])) { buildParentMap($category['categories'], $category['id'], $map); } } } /** * 根据映射表生成目标节点的路径 * @param string $targetId 要查找的节点ID * @param array $parentMap 节点-父节点映射表 * @return string|null 路径字符串,未找到则返回null */ function getPathFromMap($targetId, $parentMap) { if (!isset($parentMap[$targetId])) { return null; } $path = []; $currentId = $targetId; // 从目标节点向上追溯所有父节点 while ($currentId !== null) { $path[] = $currentId; $currentId = $parentMap[$currentId] ?? null; } // 反转路径(因为是从子到父收集的) return implode(',', array_reverse($path)); } // 构建映射表 $parentMap = []; buildParentMap($rootCategories); // 测试调用 echo getPathFromMap('c', $parentMap); // 输出:1,b,c echo "\n"; echo getPathFromMap('i', $parentMap); // 输出:2,i
代码说明
buildParentMap函数遍历整个分类树,把每个节点的父ID存入映射数组;getPathFromMap函数从目标节点开始,顺着映射表向上找所有父节点,收集到路径数组后反转,再转为字符串。
为什么你之前的代码没生效?
你之前的displayRec函数只是递归遍历了节点,但没有记录路径,也没有返回找到的结果,所以无法生成并返回你需要的路径字符串。上面的两种方案都解决了这两个核心问题。
内容的提问来源于stack exchange,提问作者Saswat
相关产品推荐
相关产品推荐

