简体   繁体   中英

Giving an example of a cycle in a directed graph

I want an algorithm that gives one instance of a cycle in a directed graph if there is any. Can anyone show me a direction? In pseudo-code, or preferably, in Ruby?

I previously asked a similar question , and following the suggestions there, I implemented Kahn's algorithm in Ruby that detects if a graph has a cycle, but I want not only whether it has a cycle, but also one possible instance of such cycle.

example_graph = [[1, 2], [2, 3], [3, 4], [3, 5], [3, 6], [6, 2]]

Kahn's algorithm

def cyclic? graph
  ## The set of edges that have not been examined
  graph = graph.dup
  n, m = graph.transpose
  ## The set of nodes that are the supremum in the graph
  sup = (n - m).uniq
  while sup_old = sup.pop do
    sup_old = graph.select{|n, _| n == sup_old}
    graph -= sup_old
    sup_old.each {|_, ssup| sup.push(ssup) unless graph.any?{|_, n| n == ssup}}
  end
  !graph.empty?
end

The above algorithm tells whether a graph has a cycle:

cyclic?(example_graph) #=> true

but I want not only that but an example of a cycle like this:

#=> [[2, 3], [3, 6], [6, 2]]

If I were to output the variable graph in the above code at the end of examination, it will give:

#=> [[2, 3], [3, 4], [3, 5], [3, 6], [6, 2]]

which includes the cycle I want, but it also includes extra edges that are irrelevant to the cycle.

I asked the same question in the math stackexchange site , and got an answer. It turned out that Tarjan's algorithm is good for solving this problem. I implemented it in Ruby as follows:

module DirectedGraph; module_function
    ## Tarjan's algorithm
    def strongly_connected_components graph
        @index, @stack, @indice, @lowlink, @scc = 0, [], {}, {}, []
        @graph = graph
        @graph.flatten(1).uniq.each{|v| strong_connect(v) unless @indice[v]}
        @scc
    end
    def strong_connect v
        @indice[v] = @index
        @lowlink[v] = @index
        @index += 1
        @stack.push(v)
        @graph.each do |vv, w|
            next unless vv == v
            if !@indice[w]
                strong_connect(w)
                @lowlink[v] = [@lowlink[v], @lowlink[w]].min
            elsif @stack.include?(w)
                @lowlink[v] = [@lowlink[v], @indice[w]].min
            end
        end
        if @lowlink[v] == @indice[v]
            i = @stack.index(v)
            @scc.push(@stack[i..-1])
            @stack = @stack[0...i]
        end
    end
end

So if I apply it to the example above, I get a list of strongly connected components of the graph:

example_graph = [[1, 2], [2, 3], [3, 4], [3, 5], [3, 6], [6, 2]]
DirectedGraph.strongly_connected_components(example_graph)
#=> [[4], [5], [2, 3, 6], [1]]

By selecting those components that are longer than one, I get the cycles:

DirectedGraph.strongly_connected_components(example_graph)
.select{|a| a.length > 1}
#=> [[2, 3, 6]]

And further if I select from the graph the edges whose both vertices are included in the components, I get the crucial edges that constitute the cycles:

DirectedGraph.strongly_connected_components(example_graph)
.select{|a| a.length > 1}
.map{|a| example_graph.select{|v, w| a.include?(v) and a.include?(w)}}
#=> [[[2, 3], [3, 6], [6, 2]]]

Depth first search, where you keep track of the visited vertices and the parent will give you the cycle. If you see an edge to a previously visited vertex then you have detected a cycle between your parent, yourself, and that vertex. A slight problem you may encounter is, if it is a cycle of length > 3, you'll only be able to tell the three vertices involved and will have to do some investigation into finding the rest of the vertices in the cycle.

For the investigation, you can start a breadth first search 'up' the tree starting from the parent and looking for the visited vertex, you should be able to find the whole cycle by doing that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM