Efficient algorithm for merging two DAGs

前端 未结 3 1701

I have two weighted DAGs (directed acyclic graphs) and need to merge them into one, so I can get a topological ordering (it could be more than two in some cases). The problem is

3条回答
  •  無奈伤痛
    2021-02-05 20:48

    I had a similar problem that I solved like so:

    I transformed my DAG into a map with a map of nodes (keyed by node id, value a collection of nodes, probably one to start) and a map of edges (keyed by source, target pair, value is a collection of edges, probably one to start). I called this normalize. The original graph was a collection of "roots" each node had a collection of children

    I could then merge two of these together by merging nodes by key and edges by key. ie: if a node did exist then cons the new node to the existing node value, if a node does not exist, then add it. same with the edges.

    This worked pretty clean but it didn't avoid cycles.

    Here is some code (clojure) that I used:

    (def empty-graph
       {:nodes {}
        :edges {}})
    
    (defn merge-graphs
      [a b]
      {:nodes (merge-with concat (get a :nodes) (get b :nodes))
       :edges (merge-with concat (get a :edges) (get b :edges))})
    
    (defn normalize-graph
      [graph]
      {:nodes (->>
                graph
                (mapcat
                  (fn [root]
                    (->>
                      root
                      (tree-seq (fn [_] true) (fn [node] (get node :children)))
                      (mapcat
                        (fn [edge]
                          (concat
                            (if-let [source (get edge "source")]
                              [[source [source]]]
                              [])
                            (if-let [target (get edge "target")]
                              [[target [target]]]
                              [])))))))
                (into {}))
       :edges (->>
                graph
                (mapcat
                  (fn [root]
                    (->>
                      root
                      (tree-seq (fn [_] true) (fn [node] (get node :children)))
                      (filter (fn [edge] (and (not (nil? (get edge "source"))) (not (nil? (get edge "target")))))) ;this sucks but is necessary
                      (map
                        (fn [edge]
                          (let [compact (dissoc edge :children)]
                            [[(get edge "source") (get edge "target")] [compact]]))))))
                (into {}))})
    

提交回复
热议问题