Сериализация графа

Я ищу простой алгоритм для «сериализации» ориентированного графа. В частности, у меня есть набор файлов с взаимозависимостями в порядке их выполнения, и я хочу найти правильный порядок во время компиляции. Я знаю, что это должно быть довольно обычное дело - компиляторы делают это постоянно, - но мой гугл-фу сегодня был слабым. Какой для этого нужно алгоритм?

Ответов (4)

Решение

Топологическая сортировка (из Википедии):

В теории графов топологическая сортировка или топологическое упорядочение ориентированного ациклического графа (DAG) - это линейное упорядочение его узлов, в котором каждый узел предшествует всем узлам, к которым он имеет исходящие ребра. Каждый DAG имеет один или несколько топологических видов.

Псевдокод:

L ← Empty list where we put the sorted elements
Q ← Set of all nodes with no incoming edges
while Q is non-empty do
    remove a node n from Q
    insert n into L
    for each node m with an edge e from n to m do
        remove edge e from the graph
        if m has no other incoming edges then
            insert m into Q
if graph has edges then
    output error message (graph has a cycle)
else 
    output message (proposed topologically sorted order: L)

Я бы ожидал, что инструменты, которым это нужно, просто пройдут дерево в глубину, и когда они попадут в лист, просто обработайте его (например, скомпилируйте) и удалите его с графа (или отметьте его как обработанный, и обработайте узлы всеми листьями. обрабатывается как листья).

Пока это DAG, этот простой обход на основе стека должен быть тривиальным.

Я придумал довольно наивный рекурсивный алгоритм (псевдокод):

Map<Object, List<Object>> source; // map of each object to its dependency list
List<Object> dest; // destination list

function resolve(a):
    if (dest.contains(a)) return;
    foreach (b in source[a]):
        resolve(b);
    dest.add(a);

foreach (a in source):
    resolve(a);

Самая большая проблема заключается в том, что он не может обнаруживать циклические зависимости - он может перейти в бесконечную рекурсию (т.е. переполнение стека ;-p). Единственный способ обойти это, что я вижу, - это превратить рекурсивный алгоритм в интерактивный с ручным стеком и вручную проверить стек на наличие повторяющихся элементов.

У кого-нибудь есть что-нибудь получше?

Если график содержит циклы, как могут существовать разрешенные порядки выполнения для ваших файлов? Мне кажется, что если график содержит циклы, значит, у вас нет решения, и это правильно сообщает вышеупомянутый алгоритм.