带节点过滤的Java通用树遍历

带节点过滤的Java通用树遍历,java,algorithm,tree,tree-traversal,Java,Algorithm,Tree,Tree Traversal,我有一个通用的树结构。 我需要一个算法来遍历它,如果它们不包含在给定的列表中,则删除一些叶子。如果从子树中删除了所有叶,则也要删除整个子树 示例树: 0 / | \ 1 2 3 / \ | / \ 4 5 6 7 8 要保留的叶:{4,6} 结果树: 0 / | 1 2 / | 4 6 输入数据结构包含在HashMap中,其中键是节

我有一个通用的树结构。 我需要一个算法来遍历它,如果它们不包含在给定的列表中,则删除一些叶子。如果从子树中删除了所有叶,则也要删除整个子树

示例树:

         0
       / |  \
      1  2   3
    /  \ |  / \
   4   5 6  7  8
要保留的叶:{4,6}

结果树:

         0
       / | 
      1  2 
    /    | 
   4     6  
输入数据结构包含在HashMap中,其中键是节点的父id,值是直接位于父节点下的节点列表(但不是递归的所有子节点)。根节点的父id为空字符串

Map<String, List<Node>> nodes;

class Node {
    private String id;
    //other members
    //getters, setters
}
映射节点;
类节点{
私有字符串id;
//其他成员
//能手,二传手
}
我想,某种递归DFS遍历算法应该可以工作,但我找不到它是如何工作的

我想,某种递归DFS遍历算法应该可以工作

完全正确。以下是如何构造此算法:

  • 请注意,该任务具有递归结构:将其应用于树的任何分支,对该分支的作用与对整个树的作用相同
  • 树枝可以修剪,也可以完全去掉
  • 递归实现将返回一个修剪过的分支;它将通过返回
    null
  • 递归函数将检查传入的
    节点
  • 如果该节点表示一个叶,则将对照我们希望保留的项目列表检查其内容
  • 如果叶不在“保留列表”中,则返回
    null
    ;否则返回叶子
  • 对于非叶分支,调用递归方法,并检查其结果
  • 如果结果为
    null
    ,则从映射中删除相应的分支;否则,用调用返回的修剪过的分支替换该分支
  • 如果在检查所有分支时,子节点的映射为空,则返回
    null

请注意,如果没有叶节点与“keep”列表匹配,则算法可能返回
null
。如果不需要这样做,请在递归实现的顶部添加一个额外的级别,并用返回空树替换顶部级别的返回。

我建议您尝试以下方法:

方法
boolean removerively(String id,Set leavesToKeep)
将从具有给定
id
的节点向下遍历到此分支

首先,我们检查当前节点是否为叶。如果叶不在
leavesToKeep
集中,我们将其删除并返回
true
,否则返回
false
。这是递归的基本情况

如果节点不是叶,则我们执行如下操作:

children.removeIf(n -> removeRecursively(n.id, leavesToKeep));
public static boolean removeRecursively(Map<String, List<Node>> tree, String id, Set<String> leavesToKeep) {
    List<Node> children = tree.get(id);
    if (children == null || children.isEmpty()) {
        if (!leavesToKeep.contains(id)) {
            tree.remove(id);
            return true;
        } else return false;
    }
    children.removeIf(n -> removeRecursively(tree, n.id, leavesToKeep));
    if (children.isEmpty()) {
        tree.remove(id);
        return true;
    } else return false;
}
是一个方便的Java8方法,用于删除满足给定谓词的所有元素。这意味着仅当该子项的所有子项也被删除时,该子项才会从列表中删除。因此,如果在
子项之后,我们应该使
Removery
返回true。removeIf
调用
子项
列表为空:

if (children.isEmpty()) {
    tree.remove(id);
    return true;
} else return false;
完整方法可能如下所示:

children.removeIf(n -> removeRecursively(n.id, leavesToKeep));
public static boolean removeRecursively(Map<String, List<Node>> tree, String id, Set<String> leavesToKeep) {
    List<Node> children = tree.get(id);
    if (children == null || children.isEmpty()) {
        if (!leavesToKeep.contains(id)) {
            tree.remove(id);
            return true;
        } else return false;
    }
    children.removeIf(n -> removeRecursively(tree, n.id, leavesToKeep));
    if (children.isEmpty()) {
        tree.remove(id);
        return true;
    } else return false;
}
publicstaticbooleanRemovery(映射树、字符串id、Set-leavestokep){
List children=tree.get(id);
if(children==null | | children.isEmpty()){
如果(!leavestokep.contains(id)){
树。删除(id);
返回true;
}否则返回false;
}
children.removeIf(n->removersively(tree,n.id,leavestokep));
if(children.isEmpty()){
树。删除(id);
返回true;
}否则返回false;
}
其中
tree
是您描述的映射,
id
是开始节点id,
leavesToKeep
是要保留的一组id。

带有接口树:

public static interface Tree<T> {
    public T getValue();

    public List<Tree<T>> children();

    public default boolean isLeaf() {
        return children().isEmpty();
    }

    public default boolean removeDeadBranches(Predicate<T> testLiveLeaf) {
        if (isLeaf()) {
            return testLiveLeaf.test(getValue());
        }
        boolean remainLife = false;
        for (Iterator<Tree<T>> it = children().iterator(); it.hasNext();) {
            if (it.next().removeDeadBranches(testLiveLeaf)) {
                remainLife = true;
            } else {
                it.remove();
            }
        }
        return remainLife;
    }
}
公共静态接口树{
公共T getValue();
公开儿童名单();
公共默认布尔isLeaf(){
返回子项().isEmpty();
}
公共默认布尔RemoveDeadBranchs(谓词testLiveLeaf){
if(isLeaf()){
返回testLiveLeaf.test(getValue());
}
布尔剩余寿命=假;
for(Iterator it=children().Iterator();it.hasNext();){
if(it.next().RemoveDeadBranchs(testLiveLeaf)){
余生=真;
}否则{
it.remove();
}
}
返老还童;
}
}
导入com.google.common.collect.Lists;
导入org.junit.Before;
导入org.junit.Test;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入java.util.List;
公共类筛选器树节点{
私有记录器Logger=LoggerFactory.getLogger(FilterTreeNode.class);
私有树节点0;
私有列表targetNode=Lists.newArrayList(“B1”、“D1”);
@以前
公共void init(){
node0=TreeNode.builder().nodeCode(“0”).nodeName(“A”).build();
TreeNode1=TreeNode.builder().nodeCode(“1”).nodeName(“B”).build();
TreeNode2=TreeNode.builder().nodeCode(“2”).nodeName(“C”).build();
TreeNode3=TreeNode.builder().nodeCode(“3”).nodeName(“D”).build();
TreeNode4=TreeNode.builder().nodeCode(“4”).nodeName(“B1”).build();
TreeNode5=TreeNode.builder().nodeCode(“5”).nodeName(“B2”).build();
TreeNode6=TreeNode.builder().nodeCode(“6”).nodeName(“C1”).build();
TreeNode7=TreeNode.builder().nodeCode(“7”).nodeName(“D1”).build();
TreeNode8=TreeNode.builder().nodeCode(“8”).nodeName(“D2”).build();
node1.setChildren(list.newArrayList(node4,node5));
node2.setChildren(list.newArrayList(node6));
node3.setChildren(list.newArrayList(node7,node8));
node0.setChildren(list.newArrayList(node1、node2、node3));
}
@试验
公共无效筛选器测试(){
info(“过滤器节点0之前:{}”,节点0);
List retNodes=filterNode(node0);
public Tree filter(Tree tree, List<Integer> ids) {
    if (tree.getNode() == null) {
        return tree;
    }
    filterNode(tree.getNode(), ids);

    return tree;
}

private boolean filterNode(Tree.TreeNode node, List<Integer> idsToFilter) {
    boolean isMatch = idsToFilter.contains(node.getId());
    node.setMatch(isMatch);
    if (CollectionUtils.isEmpty(node.getNodes()) && !isMatch) {
        return true;
    }

    node.getNodes().removeIf(treeNode -> filterNode(treeNode, idsToFilter));

    return node.getNodes().size() == 0 && !isMatch;
}