Re: [QUIZ] Perl 'Medium' Quiz: Graph Connected Components (#2006-12-29)
"Julien Quint (Pom)" <pom-gt/[email protected]> Thu, 4 Jan 2007 12:08:16 +0900
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
On Dec 30, 2006, at 4:41 , Shlomi Fish wrote:
> Here's a quiz for the new year. My sister received this challenge
> for her
> college homework, and had to write some Standard ML code to solve
> it, a task
> which I helped her with. I hope you enjoy it too.
Hi all,
here is my answer for this quizz. Nothing fancy, not thoroughly
tested but this is a well-known solution.
# Compute the connected components of the graph and return a list of
list of
# edges. Every list is a connected components. The edges are ordered
in the
# same way as in the original list of edges.
# A simple algorithm for computing connected components is to build the
# equivalence classes of vertices in the graph. Vertices belonging to
the
# same component are equivalent and belong in the same class. A
simple way
# to represent equivalence classes is a Union-Find tree. All elements
in a
# tree are equivalent, and the tree structure makes it easy to build the
# union of two equivalence classes as we just need to attach the root
of one
# the trees to any node in the other.
# The algorithm works in three steps:
# 1. Make a new tree for every vertex in the graph with this vertex
as the
# root (and only node so far.) Nodes point to their parent in a UF
tree
# and the root points to itself.
# 2. An edge between two vertices u and v means that u and v are in
the
# same component, so u and v are equivalent, which means that all
vertices
# equivalent to u are equivalent to all vertices equivalent to u.
This is
# the union of their equivalent classes, which is implemented by
attaching
# the root of v's tree to u. Once we have iterated over every edge
in the
# graph, the resulting UF forest gives us the equivalence classes of
# vertices.
# 3. We build the list of list of edges (each list being a
component) by
# going through the original list of edges (to maintain the original
# order) and finding which component the edge belongs to. For every
edge,
# looking up the root of the UF tree that one of its vertex belongs to
# gives us the index of the component in the list of list of edges,
and we
# just push that vertex into the list.
# Since we get the list of edges for the graph and not a list of
vertices,
# steps 1 and 2 can be merged.
sub connected
{
my $edges = shift;
# Step 1: initialize the UF forest for every vertex in the graph.
my $uf = { map { map { $_ => $_ } @$_ } @$edges };
# Step 2: make the union of the vertices of every edge.
$uf->{root($uf, $_->[1])} = $_->[0] for @$edges;
# Step 3: build the list of components. @components is the list
itself,
# %components is the mapping between the root of an UF-tree and
the index
# of the edge list in @components.
my @components = ();
my %components = ();
for (@$edges) {
my $root = root($uf, $_->[0]);
$components{$root} = @components if !exists $components{$root};
push @{$components[$components{$root}]}, $_;
}
return \@components;
}
# Find the root of an UF tree for a given node. We use a simple path
# compression technique which also brings up nodes that we look-up
closer to
# the root.
sub root
{
my ($uf, $u) = @_;
while ($uf->{$u} != $u) {
my $next = $uf->{$u};
$uf->{$u} = $uf->{$next};
$u = $next;
}
return $u;
}
Julien