How do I check if a directed graph is acyclic? And how is the algorithm called? I would appreciate a reference.
How do I check if a directed graph is acyclic? And how is the algorithm called? I would appreciate a reference.
Doing a simple depth-first-search is not good enough to find a cycle. It is possible to visit a node multiple times in a DFS without a cycle existing. Depending on where you start, you also might not visit the entire graph.
You can check for cycles in a connected component of a graph as follows. Find a node which has only outgoing edges. If there is no such node, then there is a cycle. Start a DFS at that node. When traversing each edge, check whether the edge points back to a node already on your stack. This indicates the existence of a cycle. If you find no such edge, there are no cycles in that connected component.
As Rutger Prins points out, if your graph is not connected, you need to repeat the search on each connected component.
As a reference, Tarjan's strongly connected component algorithm is closely related. It will also help you find the cycles, not just report whether they exist.
Lemma 22.11 on the Book Introduction to Algorithms (Second Edition) states that:
A directed graph G is acyclic if and only if a depth-first search of G yields no back edges
Just had this question in a Google interview.
You can try to sort topologically, which is O(V + E) where V is the number of vertices, and E is the number of edges. A directed graph is acyclic if and only if this can be done.
The recursively remove leaf nodes until there are none left, and if there's more than a single node left you've got a cycle. Unless I'm mistaken, this is O(V^2 + VE).
However, an efficient DFS-esque algorithm, worst case O(V + E), is:
function isAcyclic (root) {
const previous = new Set();
function DFS (node) {
previous.add(node);
let isAcyclic = true;
for (let child of children) {
if (previous.has(node) || DFS(child)) {
isAcyclic = false;
break;
}
}
previous.delete(node);
return isAcyclic;
}
return DFS(root);
}
The solution given by ShuggyCoUk is incomplete because it might not check all nodes.
def isDAG(nodes V):
while there is an unvisited node v in V:
bool cycleFound = dfs(v)
if cyclefound:
return false
return true
This has timecomplexity O(n+m) or O(n^2)
Here my implementation in pseudocode:
bool Acyclacity_Test
InitColor() //Sets to WHITE every vertex
while there is a node v in V:
if (v.color == WHITE) then
tmp = Aux_Acy(v);
if ( not tmp ) return false
return true
END
bool Aux_Acy(u)
u.color = GREY
for each node v in Adj(u)
if(v.color == GREY) return false
else if(v.color == WHITE) tmp = Aux_Acy(v)
if(!tmp) return false;
u.color = BLACK
return true
END
You can use inversion of finding cycle from my answer here https://stackoverflow.com/a/60196714/1763149
def is_acyclic(graph):
return not has_cycle(graph)