/* 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<V, E> { private Graph<V, E> g; /** * Create an instance with a reference to the graph that we will find LCAs * for */ TarjanLowestCommonAncestor(Graph<V, E> g) { this.g = g; } /** * Calculate the LCM between <code>a</code> and <code>b</code> treating * <code>start</code> as the root we want to search from. */ public V calculate(V start, V a, V b) { List<LcaRequestResponse<V>> list = new LinkedList<LcaRequestResponse<V>>(); list.add(new LcaRequestResponse<V>(a, b)); return calculate(start, list).get(0); } /** * Calculate the LCMs between a set of pairs (<code>a</code> and <code> * b</code>) treating <code>start</code> as the root we want to search from, * and setting the LCA of each pair in its LCA field */ public List<V> calculate(V start, List<LcaRequestResponse<V>> 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 <block>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. </block> // (http://en.wikipedia.org/wiki/Tarjan's_off-line_lowest_common_ancestors_algorithm) private UnionFind<V> uf = new UnionFind<V>(Collections.<V>emptySet()); // the ancestors. instead of <code>u.ancestor = x</code> we do // <code>ancestors.put(u,x)</code> private Map<V, V> ancestors = new HashMap<V, V>(); // instead of u.colour = black we do black.add(u) private Set<V> black = new HashSet<V>(); // the two vertex that we want to find the LCA for private List<LcaRequestResponse<V>> lrr; private MultiMap<V> lrrMap; private Worker(List<LcaRequestResponse<V>> lrr) { this.lrr = lrr; this.lrrMap = new MultiMap<V>(); // put in the reverse links from a and b entries back to the // LcaRequestReponse they're contained in for (LcaRequestResponse<V> 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 * <code>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 + ".";</code> * * @param u the starting node (called recursively) * * @return the LCM if found, if not null */ private List<V> 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<LcaRequestResponse<V>> requestsForNodeU = lrrMap.get(u); if (requestsForNodeU != null) { for (LcaRequestResponse<V> 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<V> result = new LinkedList<V>(); for (LcaRequestResponse<V> current : lrr) { result.add(current.getLca()); } return result; } } public static class LcaRequestResponse<V> { 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<V> extends HashMap<V, Set<LcaRequestResponse<V>>> { public Set<LcaRequestResponse<V>> getOrCreate(V key) { if (!containsKey(key)) { put(key, new HashSet<LcaRequestResponse<V>>()); } return get(key); } } } // End TarjanLowestCommonAncestor.java