Лемма: Если существует ребро V -> Y и Y также является косвенным преемником V (например, V -> W -> + Y), то ребро V -> Y транзитивно и не является частью транзитивного корня.
Метод: Отслеживайте транзитивное замыкание каждой вершины, работая от терминала к начальным вершинам в обратном топологическом порядке. Множество косвенных наследников V - это объединение транзитивных замыканий непосредственных наследников V. Транзитивное замыкание V - это объединение косвенных наследников и его непосредственных наследников.
Алгоритм:
Initialise Visited as the empty set.
For each vertex V of G,
Invoke Visit(V).
Visit(V):
If V is not in Visited,
Add V to Visited,
Initialise Indirect as the empty set,
For each edge V -> W in G,
Invoke Visit(W),
Add Closure(W) to Indirect.
Set Closure(V) to Indirect.
For each edge V -> W in G,
Add W to Closure(V),
If W is in the set Indirect,
Delete the edge V -> W from G.
Это предполагает, что у вас есть какой-то эффективный способ отслеживания наборов вершин (например, битовых карт), но я думаю, что это предположение сделано и в других алгоритмах O (V + E).
Потенциально полезным побочным эффектом является то, что он находит транзитивное замыкание каждой вершины G.