Abigail on 15 Oct 2003 12:04:07 -0400 |
On Wed, Oct 15, 2003 at 01:34:15AM -0400, Mark Dominus wrote: > > I have a bunch of nodes, which are abstract objects. Nodes have a > 'parent' method, which returns the parent node, if there is one, or > 'undef' if not. I can also compare two nodes to see if they are > equal. No node has more than one parent. > > I want to write a function which, given a list of nodes, returns the > youngest common ancestor of all the nodes in the list, or undef if > there is no common ancestor. For example, given nodes organized like > this: > > > A M > / \ | > B C N > / \ | > D E F > | > G > > The common ancestor of D and G is B; > the common ancestor of D and E is B; > the common ancestor of D and B is B; > the common ancestor of D and A is A; > the common ancestor of D and F is A; > the common ancestor of G and E is E; > the common ancestor of D and M is undefined > the common ancestor of N and M is M. > > My function to do this is 18 lines long. This is way too much code. > I am sure there must be a simpler solution. Can anyone think of a > simpler solution? I've 12 lines of code. It's the function 'lca' in the code below. Beside the 'parent' method, the objects also have a 'name' method, returning their label. I also created an extra node, with the empty string as a label, itself as a parent, and with A and M as its children. It's also assumed that no node has label '0'. Here's the code: #!/usr/bin/perl use strict; use warnings; { package Node; my %parent; my %name; sub DESTROY { my $self = shift; delete $parent {$self}; delete $name {$self} } sub new { my $class = shift; my $self = bless \my $x => $class; } sub parent { my $self = shift; if (@_) { $parent {$self} = shift; return $self; } $parent {$self} } sub name { my $self = shift; if (@_) { $name {$self} = shift; return $self; } $name {$self} } } package main; my %parents = (A => "", M => "", qw / B A C A D B E B F C G E N M /); my %node; # "Super" node with label "", which is its own parent. $node {""} = Node -> new -> name (""); $node {""} -> parent ($node {""}); # Abuse the fact keys are sorted in the tree. foreach my $name (sort keys %parents) { my $parent = $parents {$name}; $node {$name} = Node -> new -> parent ($node {$parent}) -> name ($name); } # This is the function that finds the lowest common ancestor. # For each node, create a list, starting with itself, followed by # all the ancestors, in order, ending with "". # Given two lists, pop off ancestors until we've found a difference. sub lca { my ($node1, $node2) = @_; my @parents1 = $node1; my @parents2 = $node2; while ($node1 -> name) {$node1 = $node1 -> parent; push @parents1 => $node1} while ($node2 -> name) {$node2 = $node2 -> parent; push @parents2 => $node2} while (@parents1 > 1 && @parents2 > 1 && $parents1 [-2] == $parents2 [-2]) { pop @parents1; pop @parents2; } $parents1 [-1]; } my @pairs = ([qw /D G/], [qw /D E/], [qw /D B/], [qw /D A/], [qw /D F/], [qw /G E/], [qw /D M/], [qw /N M/], [qw /E E/], [qw /A A/], [qw /A M/]); foreach my $pair (@pairs) { my ($node1, $node2) = map {$node {$_}} @$pair; printf "The common ancestor of %s and %s is %s\n", $node1 -> name, $node2 -> name, lca ($node1, $node2) -> name || "undefined"; } __END__ The common ancestor of D and G is B The common ancestor of D and E is B The common ancestor of D and B is B The common ancestor of D and A is A The common ancestor of D and F is A The common ancestor of G and E is E The common ancestor of D and M is undefined The common ancestor of N and M is M The common ancestor of E and E is E The common ancestor of A and A is A The common ancestor of A and M is undefined - **Majordomo list services provided by PANIX <URL:http://www.panix.com>** **To Unsubscribe, send "unsubscribe phl" to majordomo@lists.pm.org**
|
|