Re: [QUIZ] Perl 'Medium' Quiz: Graph Connected Components (#2006-12-29)
demerphq <[email protected]> Sat, 6 Jan 2007 18:44:54 +0100
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
On 12/29/06, Shlomi Fish <shlomif-ik1l9ssToec+JF/[email protected]> wrote: > You should implement a function called "connected" that will receive such an > input and return its connected components, where a connected component is such > that for every two nodes in it, there's a path from one to the other. So for > the graph above the function will return: > > [ > [[$A,$B],[$B,$E],[$B,$F],[$F,$E]], > [[$D,$C]], > ] > > One constraint is that the output should be ordered according to the order > of the links in the input. I.e: 1) the order of each link in each connected > component correspondence to their order in the input, and 2) the order of > the first links from each connected components corresponds to their order > in the input. > > Input: > ------ > > connected($list), where $list = \@list, and @list is made entirely of [$i,$j] > where $i and $j are integers. > > Output: > ------- > > $ret = connected($list) where: > > @comp1 = [ [$i1,$j1], [$i2, $j2], [$i3, $j3] ]; > @comp2 = [ [$k1,$l1], [$k2, $l2],... ]; > @components = ( \@comp1, \@comp2,... ); > $ret = [ @components ]; > > --------------------------- > > Have fun! Hi, below is my solution. It wasnt clear to me what should happen for input like [[1,2],[2,1]] and [[1,2],[1,2]] so I made it output [[[1,2],[2,1]]] and [[[1,2],[1,2]]] respectively in such cases. Cheers, Yves #!perl use strict; use warnings; # $AoAoT = connected($AoT) # # Take an array of tuples representing edges in a graph. # Returns an array of arrays of connected tuples, with the tuples # being in equivelent order to their order in the input. # #- # # Loop through the elements, building up a record of the vertexes # we have seen and which array we have put their edges into, merging # the lists as necessary when an edge results in a join. # # @groups stores the different groups of connected edges. # # %node2group is a map of node to the group that holds its edges. # The values are references to scalars which in turn point at an # array stored in @groups. When merging the value of the referenced # scalar is updated to point at the new list, allowing efficient # update of the mapping for all of the nodes involved. # # %must_sort tells us which lists must be sorted later due to merging. # # %id is used to track the order the tuples occur in. We have to support # things like $x=[1,2]; connected([$x,[2,3],$x,[3,4],$x]]); # # Once all of the edge tuples have been inserted into an array contained # by @groups we sort those lists that require it, sort @groups itself if # necessary and then return a reference to @groups. # # The tuples returned will be *COPIES* of the input. sub connected { my ( $input ) = @_; # the connected lists my @groups; # Key is node, val is scalar ref to index of @groups containing node my %node2group; # Key is idx into @groups, if val is true list must be sorted if defined my %must_sort; my ( $id, %id ) = ( 0 ); #For tracking when we encountered a given path foreach my $r ( @$input ) { # Duplicate links are not prohibited so we need to copy $r $r = [ @$r ]; $id{ 0 + $r } = ++$id; my $x = $node2group{$r->[0]}; my $y = $node2group{$r->[1]}; if ( !$x && !$y ) { # totally new - create a new list my $sref = []; push @groups, $sref; $x = \$sref; } elsif ( !$x || !$y ) { # only a single list - use whichever it was $x ||= $y; } elsif ( $$x != $$y ) { # different - join the lists before insertion push @{$$x}, splice @{$$y}; # move the elments from one to the other delete $must_sort{0 + $$y}; # we don't need to sort an empty list $must_sort{0 + $$x} = $$x; # we must sort the merged list $$y = $$x; # point the nodes to the merged list } # else the two lists same so theres nothing special to do push @{$$x}, $r; # insert the new edge $node2group{$_} = $x for @$r; # update the node mapping } # remove any empty lists @groups = grep { @$_ } @groups; # Did we do any merges requiring us to sort the results? if ( %must_sort ) { # sort the tuples in the groups for my $v (values %must_sort) { @$v = sort { $id{0+$a} <=> $id{0+$b} } @$v; } # then sort the groups so they are in the right order as well @groups = sort { $id{0+$a->[0]} <=> $id{0 + $b->[0]} } @groups; } # return the list of groups of edges. return \@groups; } -- perl -Mre=debug -e "/just|another|perl|hacker/"