树形结构中按nodeName检索节点并保留完整父路径的实现问题
解决树形结构中匹配节点并保留完整父路径的问题
我明白你的痛点——要从已有的树形结构里揪出符合nodeName的节点,还得保留它们从根到自身的完整父路径,同时之前的代码只能搞定子路径,父路径的处理一直卡壳。我来给你捋清楚解决方案,直接上可落地的代码:
首先先补全我们默认的TreeNode类结构(符合你给出的Java类开头,加上常规的树形节点属性):
public class TreeNode { private String nodeName; private TreeNode parent; private List<TreeNode> children = new ArrayList<>(); // 基础构造器 public TreeNode(String nodeName) { this.nodeName = nodeName; } // 省略必要的getter/setter和辅助方法 // 比如:getNodeName(), getParent(), getChildren(), setParent(), addChild() }
核心思路
要实现「匹配节点+保留完整父路径+保留子路径」的需求,我们需要三步走:
- 全树扫一遍,把所有符合条件的节点找出来:不管在树的哪个层级,先定位到所有
nodeName匹配的目标节点。 - 回溯父节点,构建根到目标节点的完整路径:对每个目标节点,从自身往上爬,直到根节点,把路径上的所有节点都收集起来。
- 把零散路径合并成新树:用一个缓存避免重复创建父节点,同时把目标节点的子树完整复制过来。
具体实现代码
第一步:查找所有匹配节点
先写一个递归遍历的工具方法,把所有符合nodeName的节点捞出来:
// 递归遍历全树,收集所有nodeName匹配的节点 private static List<TreeNode> findMatchingNodes(TreeNode root, String targetName) { List<TreeNode> matches = new ArrayList<>(); // 当前节点匹配,加入列表 if (root.getNodeName().equals(targetName)) { matches.add(root); } // 递归遍历子节点 for (TreeNode child : root.getChildren()) { matches.addAll(findMatchingNodes(child, targetName)); } return matches; }
第二步:构建带完整父路径的新树
这是核心方法,负责把匹配节点的父路径和子路径整合到新树里:
// 生成包含目标节点及其完整父、子路径的新树形结构 public static TreeNode buildTreeWithParentPaths(TreeNode root, String targetName) { List<TreeNode> matchingNodes = findMatchingNodes(root, targetName); if (matchingNodes.isEmpty()) { return null; // 没有匹配节点就返回null } // 用Map缓存已创建的节点,避免同一个父节点被重复添加 Map<String, TreeNode> nodeCache = new HashMap<>(); TreeNode newTreeRoot = null; for (TreeNode matchNode : matchingNodes) { // 回溯当前匹配节点到根的路径(从匹配节点到根) List<TreeNode> path = new ArrayList<>(); TreeNode current = matchNode; while (current != null) { path.add(current); current = current.getParent(); } // 反转路径,变成从根到匹配节点的顺序 Collections.reverse(path); // 把路径节点组装到新树中 TreeNode currentNewNode = null; for (TreeNode originalNode : path) { if (nodeCache.containsKey(originalNode.getNodeName())) { // 缓存里有就直接复用,避免重复创建 currentNewNode = nodeCache.get(originalNode.getNodeName()); } else { // 创建新节点,复制nodeName TreeNode newNode = new TreeNode(originalNode.getNodeName()); nodeCache.put(newNode.getNodeName(), newNode); // 设置父节点关系 if (currentNewNode == null) { // 第一个节点就是新树的根 newTreeRoot = newNode; } else { currentNewNode.getChildren().add(newNode); newNode.setParent(currentNewNode); } currentNewNode = newNode; } } // 把原匹配节点的子树完整复制到新节点中(保留子路径) TreeNode newMatchNode = nodeCache.get(matchNode.getNodeName()); copyChildTree(matchNode, newMatchNode, nodeCache); } return newTreeRoot; } // 递归复制子树,确保子路径完整保留 private static void copyChildTree(TreeNode originalParent, TreeNode newParent, Map<String, TreeNode> nodeCache) { for (TreeNode originalChild : originalParent.getChildren()) { TreeNode newChild = new TreeNode(originalChild.getNodeName()); nodeCache.put(newChild.getNodeName(), newChild); newParent.getChildren().add(newChild); newChild.setParent(newParent); // 递归复制孙子节点 copyChildTree(originalChild, newChild, nodeCache); } }
使用示例
假设我们有这样一棵测试树:
Root ├── A │ └── C │ └── D │ └── E └── B └── F
我们要找nodeName为"D"的节点,调用方法后得到的新树结构会是:
Root ├── A │ └── C │ └── D │ └── E
调用代码如下:
public static void main(String[] args) { // 构建测试树 TreeNode root = new TreeNode("Root"); TreeNode nodeA = new TreeNode("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); TreeNode nodeD = new TreeNode("D"); TreeNode nodeE = new TreeNode("E"); TreeNode nodeF = new TreeNode("F"); root.addChild(nodeA); root.addChild(nodeB); nodeA.addChild(nodeC); nodeC.addChild(nodeD); nodeD.addChild(nodeE); nodeB.addChild(nodeF); // 查找并构建带完整路径的新树 TreeNode resultTree = buildTreeWithParentPaths(root, "D"); }
注意事项
- 如果你的
TreeNode有其他业务属性,记得在创建新节点时把这些属性也复制过去,修改new TreeNode(...)的逻辑即可。 - 如果存在多个同名的匹配节点,这段代码会自动把所有匹配节点的父路径合并到新树中,不会出现冲突。
- 如果不需要保留匹配节点的子路径,直接去掉
copyChildTree相关的调用就行。
内容的提问来源于stack exchange,提问作者Xeshan J
相关产品推荐
相关产品推荐

