У меня была похожая проблема, которую я решил следующим образом:
Я преобразовал свою группу обеспечения доступности баз данных в карту с картой узлов (с ключом по идентификатору узла, значением набора узлов, вероятно, одного из них для запуска)карта ребер (с указанием источника, целевой пары, значение - это набор ребер, вероятно, один из них для начала).Я назвал это нормализацией.Исходный граф представлял собой коллекцию «корней», каждый узел имел коллекцию дочерних элементов
. Затем я мог объединить два из них, объединяя узлы по ключу и ребра по ключу.То есть: если узел существовал, то объединяет новый узел со значением существующего узла, если узел не существует, то добавьте его.то же самое с краями.
Это сработало довольно чисто, но не избежало циклов.
Вот код (clojure), который я использовал:
(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 {}))})