org.jgrapht.alg.TarjanLowestCommonAncestor Maven / Gradle / Ivy
/* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
package org.jgrapht.alg;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.alg.util.*;
/**
* Used to calculate Tarjan's Lowest Common Ancestors Algorithm
*
* @author Leo Crawford
*/
public class TarjanLowestCommonAncestor
{
private Graph g;
/**
* Create an instance with a reference to the graph that we will find LCAs
* for
*/
public TarjanLowestCommonAncestor(Graph g)
{
this.g = g;
}
/**
* Calculate the LCM between a
and b
treating
* start
as the root we want to search from.
*/
public V calculate(V start, V a, V b)
{
List> list =
new LinkedList>();
list.add(new LcaRequestResponse(a, b));
return calculate(start, list).get(0);
}
/**
* Calculate the LCMs between a set of pairs (a
and
* b
) treating start
as the root we want to search from,
* and setting the LCA of each pair in its LCA field
*/
public List calculate(V start, List> lrr)
{
return new Worker(lrr).calculate(start);
}
/* The worker class keeps the state whilst doing calculations. */
private class Worker
{
// The implementation of makeFind as referred to by It uses the
// MakeSet, Find, and Union functions of a disjoint-set forest.
// MakeSet(u) removes u to a singleton set, Find(u) returns the standard
// representative of the set containing u, and Union(u,v) merges the set
// containing u with the set containing v.
// (http://en.wikipedia.org/wiki/Tarjan's_off-line_lowest_common_ancestors_algorithm)
private UnionFind uf = new UnionFind(Collections.emptySet());
// the ancestors. instead of u.ancestor = x
we do
// ancestors.put(u,x)
private Map ancestors = new HashMap();
// instead of u.colour = black we do black.add(u)
private Set black = new HashSet();
// the two vertex that we want to find the LCA for
private List> lrr;
private MultiMap lrrMap;
private Worker(List> lrr)
{
this.lrr = lrr;
this.lrrMap = new MultiMap();
// put in the reverse links from a and b entries back to the
// LcaRequestReponse they're contained in
for (LcaRequestResponse r : lrr) {
lrrMap.getOrCreate(r.getA()).add(r);
lrrMap.getOrCreate(r.getB()).add(r);
}
}
/**
* Calculates the LCM as described by
* http://en.wikipedia.org/wiki/Tarjan's_off-line_lowest_common_ancestors_algorithm
* function TarjanOLCA(u) MakeSet(u); u.ancestor := u; for each v
* in u.children do TarjanOLCA(v); Union(u,v); Find(u).ancestor := u;
* u.colour := black; for each v such that {u,v} in P do if v.colour ==
* black print "Tarjan's Lowest Common Ancestor of " + u + " and " + v +
* " is " + Find(v).ancestor + ".";
*
* @param u the starting node (called recursively)
*
* @return the LCM if found, if not null
*/
private List calculate(final V u)
{
uf.addElement(u);
ancestors.put(u, u);
for (E vEdge : g.edgesOf(u)) {
if (g.getEdgeSource(vEdge).equals(u)) {
V v = g.getEdgeTarget(vEdge);
calculate(v);
uf.union(u, v);
ancestors.put(uf.find(u), u);
}
}
black.add(u);
Set> requestsForNodeU = lrrMap.get(u);
if (requestsForNodeU != null) {
for (LcaRequestResponse rr : requestsForNodeU) {
if (black.contains(rr.getB()) && rr.getA().equals(u)) {
rr.setLca(ancestors.get(uf.find(rr.getB())));
}
if (black.contains(rr.getA()) && rr.getB().equals(u)) {
rr.setLca(ancestors.get(uf.find(rr.getA())));
}
}
// once we've dealt with it - remove it (to save memory?)
lrrMap.remove(u);
}
List result = new LinkedList();
for (LcaRequestResponse current : lrr) {
result.add(current.getLca());
}
return result;
}
}
public static class LcaRequestResponse
{
private V a, b, lca;
public LcaRequestResponse(V a, V b)
{
this.a = a;
this.b = b;
}
public V getA()
{
return a;
}
public V getB()
{
return b;
}
public V getLca()
{
return lca;
}
void setLca(V lca)
{
this.lca = lca;
}
}
@SuppressWarnings("serial")
private static final class MultiMap
extends HashMap>>
{
public Set> getOrCreate(V key)
{
if (!containsKey(key)) {
put(key, new HashSet>());
}
return get(key);
}
}
}
// End TarjanLowestCommonAncestor.java