Reputation: 151
Here's the problem I'm trying to solve. Given a directed graph G, does it contain a connected subgraph that:
Intuitively, the subgraph I'm looking for consists of a downward pointing and an upward pointing tree that share the same root and together span G. I'm calling it the two-directional spanning tree problem, but it may have another name.
The dumb algorithm I've thought of is to cycle through each node in the graph, do a backwards and a forwards DFS starting at that node and then concatenate the search trees. If a two-directional spanning tree exists, I'm pretty sure this will find one on some iteration. However, it runs in O(V(V + E)) time. My intuition is that there should be a faster algorithm. Am I correct?
Upvotes: 1
Views: 186
Reputation: 183456
Warning: This answer is incomplete; the algorithm here returns "unknown" in some cases. I'm posting it only because no one else has proposed any answers in the past few days, and it's still an improvement over the proposal inside your question itself, which is to try each vertex as a candidate root, perform "backward DFS" and "forward DFS" (where the "forward DFS" is not allowed to visit any nodes that were visited by the "backward DFS"), and make sure that the all vertices are discovered by one or the other.
Specifically, there are two ways in which the below answer improves over your proposal:
To start with . . . if G is acyclic, then we can determine whether a two-directional spanning tree exists in O(V + E) time, and if so construct it in a further O(V + E) time. To see why, observe the following:
If there's a two-directional spanning tree rooted at v, then that means that for every other vertex w, there's a path either from v to w or vice versa.
If we have a topological ordering of the vertices (which we can construct in O(V + E) time), then for any given vertices v and w, there is certainly no path from v to w if w precedes v in the topological ordering, and vice versa.
This means that we can find the vertices (if any) that have paths to or from all other vertices by topologically sorting the digraph and then making two passes:
A vertex is the root of some two-directional spanning tree if and only if it's both a "forward root" and a "backward root".
Once we've found such a root v, we can construct the tree itself by doing "forward DFS" and "reverse DFS" from that root.
An important special case (whose importance will become clear below) is the case that two consecutive vertices ("consecutive" in the topological ordering, I mean) are both valid roots. In such a case, we can do "reverse DFS" from the first one and "forward DFS" from the second one to build a two-directional spanning tree where every vertex has either indegree ≤ 1 or outdegree ≤ 1.
OK, but what if G contains a cycle? Your proposed algorithm can never return "failure" in such cases, but it is at least guaranteed to find a two-directional spanning tree as long as the root itself isn't part of a cycle in G. The preceding section completely ignores that case.
To start addressing that case, note that we can use Tarjan's algorithm, which takes O(V + E) time, to derive a directed acyclic graph G′ of the strongly connected components of G. (If G is already acyclic, then G′ = G.)
To see why G′ is useful, observe the following:
If G has a two-directional spanning tree rooted at a vertex v, then G′ has a two-directional spanning tree rooted at the vertex representing the strongly connected component containing v.
Given any strongly connected digraph H and any vertex v in H, we can do "forward DFS" or "reverse DFS" out from v to find a tree that spans H and has v as its only source or its only sink, respectively. So if H is one of the strongly connected components of G, and G′ has a two-directional spanning tree where the vertex representing H has either indegree ≤ 1 or outdegree ≤ 1, then we can straightforwardly (and efficiently) construct a subgraph of H that is a subgraph of a corresponding two-directional spanning tree of G, provided the rest of G's strongly connected components cooperate as well.
So the only remaining problem is with the root of the two-directional spanning tree of G′: just because it's the root of a two-directional spanning tree of G′, that doesn't necessarily mean that the corresponding strongly connected component of G contains the root of any two-directional spanning tree of G. For example, consider this graph:
A B
↓ ↓
C ↔ D
↓ ↓
E F
This graph doesn't have any two-directional spanning tree, but the corresponding graph of strongly connected components does (with root corresponding to {C, D}).
So, in other words, we have the following algorithm:
So, as I mentioned at the outset, this algorithm requires O(V + E) time, and it deterministically returns a tree or "failure" in all cases where your proposal is deterministic plus some cases where your proposal is nondeterministic; but there are still some cases where this algorithm punts. :-/
Upvotes: 2