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

使用流API对树状结构的惰性BFS遍历

燕琨
2023-03-14

考虑我想使用Stream API遍历树状结构的一些节点(类似的问题:[1],[2],[3])。首先想到的是:

abstract class Node {
    abstract Collection<Node> getChildren();

    final Stream<Node> stream() {
        return Stream.concat(Stream.of(this), this.getChildren().stream().flatMap(Node::stream));
    }
}

上面的stream()实现具有以下特性:

  • 它几乎是懒惰的,在某种意义上,只有根节点的直接子节点将在流创建期间被检索(其他内部节点的子节点将在必要时被查询)。
  • 它显示一个DFS遍历顺序。

现在考虑我有一个大的层次结构,<代码> GeHeaveNe()/Cux>操作非常昂贵,我正在寻找任何<代码>节点< /C> >匹配一些谓词:

final Node tree = ...;
final Predicate<Node> p = ...;
tree.stream().filter(p).findAny();
  1. 如何使我的stream()实现“完全懒惰”?如果节点本身已经匹配谓词,我不希望查询节点的子节点。我正在寻找流的惰性版本。concat(),签名为(流

共有3个答案

景阳平
2023-03-14

有点丑,但这应该适用于Java8:

public static <N> Stream<N> breadthFirst(N start, Function<? super N, Stream<N>> getChildren) {
    final LinkedList<Stream<N>> generations = new LinkedList<>();
    generations.add(Stream.of(start));
    final Iterator<Stream<N>> genIterator = createIterator(generations::remove, () -> !generations.isEmpty());
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(genIterator, Spliterator.ORDERED), false)
            .flatMap(Function.identity())
            .distinct() // avoids loops
            .peek(n -> generations.add(getChildren.apply(n)));
}

public static <E> Iterator<E> createIterator(Supplier<E> supplier, BooleanSupplier hasNext) {
    return new Iterator<E>() {
        @Override
        public boolean hasNext() {
            return hasNext.getAsBoolean();
        }
        @Override
        public E next() {
            return supplier.get();
        }
    };
}

这里的想法是,您需要保留对后续代的引用,因此我们创建一个列表以在处理时保存它们。使用Java9,您将能够用流替换自定义迭代器代码。生成(generations::poll)。takeWhile(Objects::nonNull)

栾景胜
2023-03-14

您可以使用拆分器库和低级流支持原语。然后,您可以在节点上提供一个迭代器,该迭代器只逐个使用节点。

return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<Node>()
{
    @Override
    public boolean hasNext()
    {
        // to implement
        return ...;
    }

    @Override
    public ContentVersion next()
    {
        // to implement
        return ...;
    }
}, 0 ), false );

陆俊迈
2023-03-14

不幸的是,我只能回答你的第一个问题。同样,flatMap是您在这里的朋友。它使我们能够创建一个稍微不同的concat方法,该方法接受stream-供应商s,而不仅仅是streams:

abstract class Node {
    abstract Collection<Node> getChildren();

    Stream<Node> lazyTraverse() {
        return Node.concat(() -> Stream.of(this),
                           () -> getChildren().stream().flatMap(Node::lazyTraverse));
    }

    static Stream<Node> concat(Supplier<Stream<Node>> a, Supplier<Stream<Node>> b) {
        return Stream.of(a, b).flatMap(Supplier::get);
    }
}

一个更好的解决方案是,如果您可以用某种返回惰性流的机制替换getChildren

关于你的第二个问题:

我不知道是否有BFS遍历算法以优雅的方式使用流API,但我倾向于说“不”,因为BFS通常需要额外的内存来存储所有已经访问但尚未遍历的节点。

 类似资料:
  • 我正在写一篇关于Java流API的文章。我已经阅读了Stream的整个软件包文档,并在这里查看了类似的问题。 如果我说:“直到终端操作被命中,才会评估流上的中间操作,这将实际执行它们,”我是否正确?我在StackOverflow上看到了混合答案,而且每个中间操作都返回一个Stream,所以我想知道它是否只是返回自己,然后只是跟踪要执行的中间操作。这就是“懒惰评估/执行”的意思吗? 下面的javad

  • 我在研究广度优先搜索或BFS算法时,突然想到了一个主意。我展示了我实现了BFS的图的树形结构。现在也许我可以用链表以不同的方式显示树结构,但是我想修改我用来显示树结构的BFS方法 上面给出的是我的BFS方法,有人可以帮助我让我知道我必须对代码进行哪些确切的修改,以便我获得所需的输出 例如,假设给定的邻接矩阵如下: 这个图的树形结构应该是这样的

  • 我必须创建两个类:NonBinaryTree和SingleNode类,包含一些处理节点和整个树的方法(在NonBinaryTree类中)。我在使用队列(先进先出类型)实现非二叉树的BFS(层次顺序)遍历时遇到过问题。由于二叉树有很多资源,每个节点最多有两个子节点,我还没有找到任何可以帮助我解决非二叉树问题的资源。 到目前为止,我做了这个代码: 我的树: 在此处输入图像描述 我需要按以下顺序处理节点

  • 我正在尝试在UI上显示树结构。但是得到一个错误。 无法绑定到“target”,因为它不是“i”的已知属性。(“v*ngFor=”让文件项 虽然这个属性存在于我的json文件中,如下所示: 我的html文件如下: 我无法找出这次失败的原因。我已经尝试了所有可能的条件,我可以申请使它工作。请帮我解决这个问题

  • 我有一个类别树,由以下内容表示。 这给出了一个dataframe,如下所示: 树中最高的节点的parent_id等于-1,因此树可以用图形表示如下: 我需要生成以下DataFrame。 该树是动态生成的,可以具有任意数量的级别,因此下面的树 应产生以下结果:

  • 递归树组,父节点selected为true且partiallySelected为fasle的时候,所有子节点childrens下的节点的selected则也修改为true syncChildrenStatus(arr)后 希望得到的