JavaScript如何通过子节点id查询树形结构的完整父节点路径
问题排查
你原有代码的问题主要出在这几点:
- 全局声明的
parent数组没有回溯逻辑,遍历到错误分支时推入的节点id不会被移除,会导致路径冗余 - 找到目标节点时,没有把当前节点自身的id加入路径
- 递归调用后没有对返回值做判断,即使子层找到目标也不会向上返回结果,导致最终拿到undefined
- 没有处理遍历完所有子节点都没找到的情况,路径不会回滚
正确实现代码
const findNodePath = (tree, targetId) => { // 递归遍历函数,path用来记录当前遍历路径 const dfs = (nodes, path) => { for (const node of nodes) { // 先把当前节点id加入路径 path.push(node.attr.id) // 命中目标直接返回路径副本 if (node.attr.id === targetId) { return [...path] } // 有子节点就递归查找子层 if (node.children?.length) { const childRes = dfs(node.children, path) // 子层找到结果就向上返回 if (childRes) return childRes } // 当前分支没找到,回溯:弹出当前节点id path.pop() } // 当前分支所有节点都没找到,返回null return null } const pathArr = dfs(tree, []) // 找到路径就按要求拼接成字符串,没找到返回空字符串 return pathArr ? pathArr.join(' -> ') : '' } // 测试用例 let arr = [{"attr": {"id": "nf-bsf","name": "BSF","sequence": 1},"children": [{"attr": {"id": "bsfGeneralConfig","name": "General Configurations","sequence": 10},"children": [{"attr": {"id": "services/bsf/bsfGlobalCfg","name": "General Settings","topic": "bsf.global.cfg","sequence": 10}}, {"attr": {"id": "configurations/bsf/sbiErrorCodes","name": "SBI Error Codes","topic": "bsf.sbi.errorcodes","sequence": 20}}]}, {"attr": {"id": "services/bsf/conLoggingLevel","name": "Logging Level","topic": "consistent.logging.cfg.topics","sequence": 20}}, {"attr": {"id": "bsfServices","name": "Service Configurations","sequence": 30},"children": [{"attr": {"id": "services/bsf/managementService","name": "Management Service","topic": "bsf.managementservice","sequence": 10}}]}, {"attr": {"id": "bsfConfig","name": "Diameter Configurations","sequence": 40},"children": [{"attr": {"id": "services/bsf/diamSetting","name": "Settings","topic": "common.diamsetting","sequence": 10}}, {"attr": {"id": "configurations/bsf/diamPeerNode","name": "Peer Nodes","topic": "common.public.diampeernode","sequence": 20}}, {"attr": {"id": "services/bsf/diamRoutingTable","name": "Routing Table","topic": "common.public.diamroutingtable","sequence": 30}}, {"attr": {"id": "configurations/bsf/diamLoadShedding","name": "Load Shedding Profiles","topic": "common.public.diamloadshedding","sequence": 40}}, {"attr": {"id": "configurations/bsf/diamMessagePriority","name": "Message Priority Profiles","topic": "common.public.diammessagepriority","sequence": 50}}]}, {"attr": {"id": "bsf-sessionViewer","name": "Session Viewer","sequence": 50}}, {"attr": {"id": "administration","name": "Administration","sequence": 60},"children": [{"attr": {"id": "bsfBulkExportImport","name": "Export & Import","sequence": 10}}]}]}]; console.log(findNodePath(arr, "services/bsf/diamSetting")) // 输出结果:nf-bsf -> bsfConfig -> services/bsf/diamSetting
实现逻辑说明
- 采用带回溯的深度优先遍历,每进入一个节点就把id加入当前路径,遍历完当前节点的所有子分支都没找到目标,就把当前节点id从路径中移除,避免干扰其他分支的遍历
- 命中目标时直接返回路径的副本,避免后续回溯操作修改最终结果
- 递归层只要拿到子层返回的有效路径就直接向上传递,终止其他不必要的遍历
内容的提问来源于stack exchange,提问作者misbha afreen
相关产品推荐
相关产品推荐

