You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将GraphStream中AStar求得的Path转为Graph或移除非路径节点?

嘿,我刚好之前在GraphStream里处理过类似的需求,咱们一步步来解决这个问题:

首先得明确:GraphStream的NodeEdge对象是和所属的原始图绑定的,不能直接把一个图的节点/边对象添加到另一个图,这就是你之前尝试graph.addNode(n)失败的原因。不过有两种高效的方案,对应你两种不同的需求:

方案1:修改原图,移除非路径节点/边(高效,无需复制属性)

如果你只想保留原始图中最短路径的部分,这个方法最省事儿,不用重新创建任何节点/边,直接移除不需要的元素即可:

步骤:

  1. 先获取路径中所有节点的ID集合,用来判断哪些节点需要保留:
    Set<String> pathNodeIds = p.getNodeSet().stream()
        .map(Node::getId)
        .collect(Collectors.toSet());
    
  2. 收集并移除所有不在路径中的节点(注意不能直接遍历原图节点时移除,会触发并发修改异常):
    List<Node> nodesToRemove = new ArrayList<>();
    for (Node node : graph) {
        if (!pathNodeIds.contains(node.getId())) {
            nodesToRemove.add(node);
        }
    }
    nodesToRemove.forEach(graph::removeNode);
    

    提示:移除节点时,GraphStream会自动删除该节点关联的所有边,所以这一步后,原图剩下的边都是路径节点之间的边。

  3. (可选)如果需要精准保留路径上的边(而非路径节点之间的所有边),可以再移除多余的边:
    Set<Edge> pathEdges = p.getEdgeSet();
    List<Edge> edgesToRemove = new ArrayList<>();
    for (Edge edge : graph.getEdgeSet()) {
        if (!pathEdges.contains(edge)) {
            edgesToRemove.add(edge);
        }
    }
    edgesToRemove.forEach(graph::removeEdge);
    

方案2:创建全新的路径图(独立于原图,适合需要保留原图的场景)

如果你需要一个完全独立的只包含最短路径的新图,就需要复制路径上的节点/边及其属性。虽然需要手动复制属性,但可以写个工具方法简化操作:

步骤:

  1. 先创建一个新的空图:
    Graph pathGraph = new SingleGraph("ShortestPathGraph");
    
  2. 写一个通用的属性复制工具方法(避免重复代码):
    private static void copyElementAttributes(Element source, Element target) {
        for (String attrKey : source.getAttributeKeySet()) {
            target.setAttribute(attrKey, source.getAttribute(attrKey));
        }
    }
    
  3. 复制路径上的节点及其所有属性:
    for (Node originalNode : p.getNodeSet()) {
        Node newNode = pathGraph.addNode(originalNode.getId());
        copyElementAttributes(originalNode, newNode);
    }
    
  4. 复制路径上的边及其所有属性(注意保留边的方向):
    for (Edge originalEdge : p.getEdgeSet()) {
        Edge newEdge = pathGraph.addEdge(
            originalEdge.getId(),
            originalEdge.getSourceNode().getId(),
            originalEdge.getTargetNode().getId(),
            originalEdge.isDirected()
        );
        copyElementAttributes(originalEdge, newEdge);
    }
    

为什么没有更“一键式”的方法?

目前GraphStream并没有内置的PathGraph的API,因为路径本质上只是节点和边的集合,而每个图的底层实现可能不同(比如SingleGraph vs MultiGraph)。不过上面两种方案已经是最高效的实现了:

  • 方案1的时间复杂度是O(N+E),只需要遍历一次原图的节点和边;
  • 方案2的时间复杂度是O(Pn+Pe),其中Pn是路径节点数,Pe是路径边数,比遍历整个原图要快得多。

内容的提问来源于stack exchange,提问作者simonalexander2005

火山引擎 最新活动