当前位置: 首页 > 知识库问答 >
问题:

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

南门新荣
2023-03-14

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

示例树:

         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
}

我想,某种递归DFS遍历算法应该可以工作,但我不知道它是如何工作的。

共有3个答案

裴欣然
2023-03-14

使用接口树:

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;
    }
}
高朝明
2023-03-14

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

完全正确。下面是你如何构建这个算法:

  • 观察任务是否具有递归结构:将其应用于树的任何分支对分支的操作与要对整个树执行的操作相同
  • 可以修剪分支,也可以完全删除分支
  • 递归实现将返回修剪的分支;它将通过返回 null 来发出删除分支的信号。
  • 递归函数将检查传入的节点
  • 如果节点代表一个叶子,则其内容将根据我们希望保留的项目列表进行检查
  • 如果叶子不在“保留列表”上,则返回 null;否则返回叶子
  • 对于非叶枝,调用递归方法,并检查其结果
  • 如果结果为 null,则从映射中删除相应的分支;否则,将分支替换为从调用返回的修剪分支
  • 如果在检查所有分支时子节点的映射为空,则返回 null

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

蓝飞
2023-03-14

我建议您尝试以下方法:

方法布尔远程递归(String id, Set

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

如果节点不是叶子,那么我们做这样的事情:

children.removeIf(n -> removeRecursively(n.id, leavesToKeep));

RemoveIF是一个方便的Java8方法,用于删除所有满足给定谓词的元素。这意味着只有当它的所有子级也被删除时,子级才会从列表中删除。因此,如果在children.removeIf调用子级列表为空时,我们应该使RemoveRecurely返回true:

if (children.isEmpty()) {
    tree.remove(id);
    return true;
} else return false;

完整的方法可能如下所示:

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;
}

其中tree是您描述的映射,id是开始节点id,leavesToKee是一组要保留的id。

 类似资料:
  • 假设我有一个简单的二叉树节点类,如下所示: 如何添加一个能够递归遍历任何大小的树的方法,从左到右访问每个现有节点,而无需重新访问已遍历的节点? 这行得通吗?

  • 在具有父子指针的通用树结构中,是否可以在不遍历完整树的情况下遍历叶节点?例如,从最左边的叶节点开始。想法是在深树上进行优化。

  • 我一直在尝试在工作中实现Autosys作业计划的树表示。由于每个作业(流程)可以有一个或多个依赖作业,因此我决定使用n元树实现,以便映射流程。我使用java集合来实现同样的目标。 Q1(已解决):目前的问题是显示功能被挂在一个无限循环中。我尝试寻找现有的线程,但无法遇到使用迭代器进行遍历的示例。 Q2:(打开)显示功能以后序方式遍历树。我希望以一种级别顺序的方式遍历它,其中节点以级别为基础进行打印

  • 这是一个相当简单的问题,我注意到当我表示一棵树时,无论我用哪种方式(后排序,按顺序,前排序)树叶总是以相同的顺序出现,从左到右。 我只是想知道为什么,这是有原因的吗? 我刚开始研究它们,就想到了这个。 编辑: 我有一棵这样的树: 叶节点为:D、E和F 预购顺序为:A、B、D、C、E、F 顺序是:D,B,A,E,C,F 后序是:D,B,E,F,C,A 叶子节点总是从左到右出现,不管我选择哪个顺序,问

  • 试图遍历一棵树并为我的数组获取空值。我需要遍历只允许访问节点类的类定义中没有根的右和左子级的树。 它需要输入 这应该返回[1,2,4,3,5],我得到了[]。我也尝试过像这样循环 这也不管用。这也会给我一个[]数组。遍历应该从左到右在树高(即树高)指示的树级别上打印树。有什么想法吗?

  • 我试图使用Gremlin从一个起始节点向外遍历到连接X度内的所有连接节点。连接的方向无关紧要,所以我使用了函数。我还希望能够防止遍历与特定标签相交。这是一个示例图。 到目前为止,我进行的遍历如下所示: 然而,这并不是我所寻找的。我想要一些实际上可以防止遍历者在必须跨越指定边缘时触及顶点的东西。我当前的实现过滤具有传入边缘的顶点,但在某些情况下,如果遍历者跨越不同的边缘到达那里,我可能仍然希望该顶点