Тот факт, что наш граф является ациклическим, значительно упрощает эту проблему.
Топологическая сортировка может дать нам порядок вершин такой, что если i < j , то не существует ребра от v j до v i . Мы перечислили вершины так, что все ребра в нашем списке идут вперед.v1,v2,…,vni<jvjvi
(отредактировано, чтобы исправить анализ и дать немного более быстрый алгоритм)
Теперь мы просто вернемся назад по этому списку, начиная с последней вершины . Транзитивное замыкание v n само по себе. Также добавьте v n к транзитивному замыканию каждой вершины с ребром в v n .vnvnvnvn
Для каждой другой вершины , идущей от конца назад, сначала добавьте v i к своему собственному транзитивному замыканию, затем добавьте все в транзитивном замыкании v i к транзитивному замыканию всех вершин с ребром к v i .vivivivi
Время выполнения в худшем случае с n числом вершин и m ∈ O ( n 2 ) числом ребер. Топологическая сортировка занимает время O ( n + m ) . Затем мы выполняем еще одну O ( m n ) работу в обратном проходе: когда мы идем назад по списку, для каждого ребра мы должны добавить до nO(n+m+nm)=O(n3)nm∈O(n2)O(n+m)O(mn)n вершины к чьему-то транзитивному замыканию.
Обратите внимание, что вы можете получить хорошее ускорение с постоянным коэффициентом, представляя транзитивное замыкание каждого с помощью битовых массивов. Скажем, у вас было только ; тогда вы бы использовали один 64-битный тип int, где бит i равен 1, если i находится в моем транзитивном замыкании, и 0 в противном случае. Тогда та часть , где мы добавляем все в я «s транзитивного замыкания на J » S очень быстро: Мы просто взять гр J | = с я . (Двоичная операция ИЛИ.)n=64iiijcjci
Для вам придется хранить их в массивах и выполнять некоторую арифметику, но это будет намного быстрее, чем набор объектов.n>64
OO(n3)