Are you looking for an abstract algorithm or a concrete implementation?
–
Mark JohnsonFeb 24 '09 at 22:26

Another case in favor of some way to "fix" wrong answers on SO.
–
SparrFeb 24 '09 at 22:33

2

So, umm, I am mostly interested in the time needed to find it. So, I just need the abstract algorithm.
–
nes1983Feb 24 '09 at 22:39

you must traverse all edges and check all vertices so lower bound is O(|V| + |E|). DFS and BFS are both the same complexity but DFS is easier to code if you have recursion as that manages the stack for you...
–
ShuggyCoUkFeb 24 '09 at 22:43

DFS is not the same complexity. Consider the graph with nodes { 1 .. N }, and edges in the form { (a, b) | a < b }. That graph is acyclic, and yet DFS would be O(n!)
–
FryGuyFeb 25 '09 at 3:04

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.

BTW: An edge that "points back to a node already on your stack" is often called a "back edge" in the literature, for obvious reasons. And yes, this may be simpler than sorting the graph topologically, especially if you need to do a DFS anyway.
–
sleskeSep 30 '09 at 15:40

For the graph to be acyclic, you say that each connected component must contain a node with only outgoing edges. Can you recommend an algorithm to find the connected components (as opposed to "strongly" connected components) of a directed graph, for use by your main algorithm?
–
kostmoSep 21 '10 at 4:17

@kostmo, if the graph has more than one connected component, then you will not visit all the nodes in your first DFS. Keep track of the nodes you've visited, and repeat the algorithm with unvisited nodes until you reach them all. This is basically how a connected components algorithm works anyway.
–
Jay ConrodSep 22 '10 at 1:07

1

While the intent of this answer is correct, the answer is confusing if using a stack-based implementation of DFS: the stack used to implement DFS will not contain the correct elements to test against. It's necessary to add an additional stack to the algorithm used to keep track of the set of ancestor nodes.
–
Theodore MurdockApr 2 '12 at 21:18

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.

For a graph with a single root node you send that node and an empty hashset

For a graph having multiple root nodes you wrap this in a foreach over those nodes and pass a new empty hashset for each iteration

When checking for cycles below any given node, just pass that node along with an empty hashset

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.

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

The idea is like this : a normal dfs algorithm with an array to keep track of visited nodes , and an additional array which serves as a marker for the nodes that led to the current node,so that when ever we execute a dfs for a node we set its corresponding item in the marker array as true ,so that when ever an already visited node encountered we check if its corresponding item in the marker array is true , if its true then its one of the nodes that let to itself
(hence a cycle) , and the trick is whenever a dfs of a node returns we set its corresponding marker back to false , so that if we visited it again from another route we don't get fooled .