How do I check if a directed graph is acyclic? And how is the algorithm called? I would appreciate a reference.
I would try to sort the graph topologically, and if you can't, then it has cycles.
I know this is an old topic but for future searchers here is a C# implementation I created (no claim that it's most efficient!). This is designed to use a simple integer to identify each node. You can decorate that however you like provided your node object hashes and equals properly.
For Very deep graphs this may have high overhead, as it creates a hashset at each node in depth (they are destroyed over breadth).
You input the node from which you want to search and the path take to that node.
When checking for cycles below any given node, just pass that node along with an empty hashset
private bool FindCycle(int node, HashSet<int> path)
{
if (path.Contains(node))
return true;
var extendedPath = new HashSet<int>(path) {node};
foreach (var child in GetChildren(node))
{
if (FindCycle(child, extendedPath))
return true;
}
return false;
}
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);
}
Here is my ruby implementation of the peel off leaf node algorithm.
def detect_cycles(initial_graph, number_of_iterations=-1)
# If we keep peeling off leaf nodes, one of two things will happen
# A) We will eventually peel off all nodes: The graph is acyclic.
# B) We will get to a point where there is no leaf, yet the graph is not empty: The graph is cyclic.
graph = initial_graph
iteration = 0
loop do
iteration += 1
if number_of_iterations > 0 && iteration > number_of_iterations
raise "prevented infinite loop"
end
if graph.nodes.empty?
#puts "the graph is without cycles"
return false
end
leaf_nodes = graph.nodes.select { |node| node.leaving_edges.empty? }
if leaf_nodes.empty?
#puts "the graph contain cycles"
return true
end
nodes2 = graph.nodes.reject { |node| leaf_nodes.member?(node) }
edges2 = graph.edges.reject { |edge| leaf_nodes.member?(edge.destination) }
graph = Graph.new(nodes2, edges2)
end
raise "should not happen"
end
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
Solution1: Kahn algorithm to check cycle. Main idea: Maintain a queue where node with zero in-degree will be added into queue. Then peel off node one by one until queue is empty. Check if any node's in-edges are existed.
Solution2: Tarjan algorithm to check Strong connected component.
Solution3: DFS. Use integer array to tag current status of node: i.e. 0 --means this node hasn't been visited before. -1 -- means this node has been visited, and its children nodes are being visited. 1 -- means this node has been visited, and it's done. So if a node's status is -1 while doing DFS, it means there must be a cycle existed.