All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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
     */
    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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy