# This file contains the algorithms for the connected components of an
# undirected graph (each_connected_component) and strongly connected components
# for directed graphs (strongly_connected_components).
#
require 'rgl/traversal'
module RGL
module Graph
# Compute the connected components of an undirected graph using a DFS-based
# approach. A connected component of an undirected graph is a set of
# vertices that are all reachable from each other.
#
# The function is implemented as an iterator which calls the client with an
# array of vertices for each component.
#
# It raises an exception if the graph is directed.
def each_connected_component
raise NotUndirectedError, "each_connected_component only works for undirected graphs." if directed?
comp = []
vis = DFSVisitor.new(self)
vis.set_finish_vertex_event_handler {|v| comp << v}
vis.set_start_vertex_event_handler {|v|
yield comp unless comp.empty?
comp = []
}
depth_first_search(vis) {|v|}
yield comp unless comp.empty?
end
# This GraphVisitor is used by strongly_connected_components to compute the
# strongly connected components of a directed graph.
#
#
class TarjanSccVisitor < DFSVisitor
attr_reader :comp_map
# Creates a new TarjanSccVisitor for graph _g_, which should be directed.
def initialize(g)
super g
@root_map = {}
@comp_map = {}
@discover_time_map = {}
@dfs_time = 0
@c_index = 0
@stack = []
end
def handle_examine_vertex(v)
@root_map[v] = v
@comp_map[v] = -1
@dfs_time += 1
@discover_time_map[v] = @dfs_time
@stack.push v
end
def handle_finish_vertex(v)
# Search adjacent vertex w with earliest discover time
root_v = @root_map[v]
graph.each_adjacent(v) do |w|
if @comp_map[w] == -1
root_v = min_discover_time(root_v,@root_map[w])
end
end
@root_map[v] = root_v
if root_v == v # v is topmost vertex of a SCC
begin # pop off all vertices until v
w = @stack.pop
@comp_map[w] = @c_index
end until w == v
@c_index += 1
end
end
# Return the number of components found so far.
def num_comp
@c_index
end
private
def min_discover_time(u,v)
@discover_time_map[u] < @discover_time_map[v] ? u : v
end
end # TarjanSccVisitor
# This is Tarjan's algorithm for strongly connected components
# from his paper "Depth first search and linear graph algorithms".
# It calculates the components in a single application of DFS.
# We implement the algorithm with the help of the DFSVisitor
# TarjanSccVisitor.
#
# === Definition
#
# A strongly connected component of a directed graph G=(V,E) is a
# maximal set of vertices U which is in V such that for every pair of
# vertices u and v in U, we have both a path from u to v and path from v to
# u. That is to say that u and v are reachable from each other.
#
# @Article{Tarjan:1972:DFS,
# author = "R. E. Tarjan",
# key = "Tarjan",
# title = "Depth First Search and Linear Graph Algorithms",
# journal = "SIAM Journal on Computing",
# volume = "1",
# number = "2",
# pages = "146--160",
# month = jun,
# year = "1972",
# CODEN = "SMJCAT",
# ISSN = "0097-5397 (print), 1095-7111 (electronic)",
# bibdate = "Thu Jan 23 09:56:44 1997",
# bibsource = "Parallel/Multi.bib, Misc/Reverse.eng.bib",
# }
#
# The output of the algorithm is recorded in a TarjanSccVisitor _vis_.
# vis.comp_map will contain numbers giving the component ID assigned to each
# vertex. The number of components is vis.num_comp.
def strongly_connected_components
raise NotDirectedError, "strong_components only works for directed graphs." unless directed?
vis = TarjanSccVisitor.new(self)
depth_first_search(vis) {|v|}
vis
end
end
end