Rotate the graph by constructing a set of back edges and a list of leaf nodes.
Perform a topological view of the reversed edges using the starting nodes of the leaf (which are now the root nodes).
Build the inverse graph based on the inverse edges, starting at the end of the sorted list. Since the nodes are built in the reverse topological order, you are guaranteed to create children of this node before building the node, so it is possible to create an immutable representation.
This is either O (N) if you use structures for an intermediate view that track all links in both directions associated with a node, or O (NlnN) if you use sorting to find all node links. For small graphs or languages โโthat do not suffer from, you can simply construct the graph lazily, rather than explicitly performing topological sorting. So it depends a little on what you implement all this in how much it would be.
A -> (B -> G, C -> (E -> F, D -> F)) original roots: [ A ] original links: [ AB, BG, AC, CE, EF, CD, DF ] reversed links: [ BA, GB, CA, EC, FE, DC, FD ] reversed roots: [ G, F ] reversed links: [ BA, CA, DC, EC, FE, FD, GB ] (in order of source) topologically sorted: [ G, B, F, E, D, C, A ] construction order : A, C->A, D->C, E->C, F->(D,E), B->A, G->B