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

org.jgrapht.alg.StrongConnectivityInspector Maven / Gradle / Ivy

/* ==========================================
 * JGraphT : a free Java graph-theory library
 * ==========================================
 *
 * Project Info:  http://jgrapht.sourceforge.net/
 * Project Creator:  Barak Naveh (http://sourceforge.net/users/barak_naveh)
 *
 * (C) Copyright 2003-2008, by Barak Naveh and Contributors.
 *
 * 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.
 */
/* --------------------------
 * StrongConnectivityInspector.java
 * --------------------------
 * (C) Copyright 2005-2008, by Christian Soltenborn and Contributors.
 *
 * Original Author:  Christian Soltenborn
 *
 * $Id$
 *
 * Changes
 * -------
 * 2-Feb-2005 : Initial revision (CS);
 * 5-Feb-2007 : fixed NullPointerException (CS);
 * 1-Apr-2008 : Reduced memory consumption (CS);
 *
 */
package org.jgrapht.alg;

import java.util.*;

import org.jgrapht.*;
import org.jgrapht.graph.*;


/**
 * 

Complements the {@link org.jgrapht.alg.ConnectivityInspector} class with * the capability to compute the strongly connected components of a directed * graph. The algorithm is implemented after "Cormen et al: Introduction to * agorithms", Chapter 22.5. It has a running time of O(V + E).

* *

Unlike {@link org.jgrapht.alg.ConnectivityInspector}, this class does not * implement incremental inspection. The full algorithm is executed at the first * call of {@link StrongConnectivityInspector#stronglyConnectedSets()} or {@link * StrongConnectivityInspector#isStronglyConnected()}.

* * @author Christian Soltenborn * @author Christian Hammer * @since Feb 2, 2005 */ public class StrongConnectivityInspector { // the graph to compute the strongly connected sets for private final DirectedGraph graph; // stores the vertices, ordered by their finishing time in first dfs private LinkedList> orderedVertices; // the result of the computation, cached for future calls private List> stronglyConnectedSets; // the result of the computation, cached for future calls private List> stronglyConnectedSubgraphs; // maps vertices to their VertexData object private Map> vertexToVertexData; /** * The constructor of the StrongConnectivityInspector class. * * @param directedGraph the graph to inspect * * @throws IllegalArgumentException */ public StrongConnectivityInspector(DirectedGraph directedGraph) { if (directedGraph == null) { throw new IllegalArgumentException("null not allowed for graph!"); } graph = directedGraph; vertexToVertexData = null; orderedVertices = null; stronglyConnectedSets = null; stronglyConnectedSubgraphs = null; } /** * Returns the graph inspected by the StrongConnectivityInspector. * * @return the graph inspected by this StrongConnectivityInspector */ public DirectedGraph getGraph() { return graph; } /** * Returns true if the graph of this * StronglyConnectivityInspector instance is strongly connected. * * @return true if the graph is strongly connected, false otherwise */ public boolean isStronglyConnected() { return stronglyConnectedSets().size() == 1; } /** * Computes a {@link List} of {@link Set}s, where each set contains vertices * which together form a strongly connected component within the given * graph. * * @return List of Set s containing the strongly * connected components */ public List> stronglyConnectedSets() { if (stronglyConnectedSets == null) { orderedVertices = new LinkedList>(); stronglyConnectedSets = new Vector>(); // create VertexData objects for all vertices, store them createVertexData(); // perform the first round of DFS, result is an ordering // of the vertices by decreasing finishing time for (VertexData data : vertexToVertexData.values()) { if (!data.isDiscovered()) { dfsVisit(graph, data, null); } } // 'create' inverse graph (i.e. every edge is reversed) DirectedGraph inverseGraph = new EdgeReversedGraph(graph); // get ready for next dfs round resetVertexData(); // second dfs round: vertices are considered in decreasing // finishing time order; every tree found is a strongly // connected set for (VertexData data : orderedVertices) { if (!data.isDiscovered()) { // new strongly connected set Set set = new HashSet(); stronglyConnectedSets.add(set); dfsVisit(inverseGraph, data, set); } } // clean up for garbage collection orderedVertices = null; vertexToVertexData = null; } return stronglyConnectedSets; } /** *

Computes a list of {@link DirectedSubgraph}s of the given graph. Each * subgraph will represent a strongly connected component and will contain * all vertices of that component. The subgraph will have an edge (u,v) iff * u and v are contained in the strongly connected component.

* *

NOTE: Calling this method will first execute {@link * StrongConnectivityInspector#stronglyConnectedSets()}. If you don't need * subgraphs, use that method.

* * @return a list of subgraphs representing the strongly connected * components */ public List> stronglyConnectedSubgraphs() { if (stronglyConnectedSubgraphs == null) { List> sets = stronglyConnectedSets(); stronglyConnectedSubgraphs = new Vector>(sets.size()); for (Set set : sets) { stronglyConnectedSubgraphs.add( new DirectedSubgraph( graph, set, null)); } } return stronglyConnectedSubgraphs; } /* * Creates a VertexData object for every vertex in the graph and stores * them * in a HashMap. */ private void createVertexData() { vertexToVertexData = new HashMap>(graph.vertexSet().size()); for (V vertex : graph.vertexSet()) { vertexToVertexData.put( vertex, new VertexData2(vertex, false, false)); } } /* * The subroutine of DFS. NOTE: the set is used to distinguish between 1st * and 2nd round of DFS. set == null: finished vertices are stored (1st * round). set != null: all vertices found will be saved in the set (2nd * round) */ private void dfsVisit( DirectedGraph visitedGraph, VertexData vertexData, Set vertices) { Deque> stack = new ArrayDeque>(); stack.add(vertexData); while (!stack.isEmpty()) { VertexData data = stack.removeLast(); if (!data.isDiscovered()) { data.setDiscovered(true); if (vertices != null) { vertices.add(data.getVertex()); } stack.add(new VertexData1(data, true, true)); // follow all edges for (E edge : visitedGraph.outgoingEdgesOf(data.getVertex())) { VertexData targetData = vertexToVertexData.get( visitedGraph.getEdgeTarget(edge)); if (!targetData.isDiscovered()) { // the "recursion" stack.add(targetData); } } } else if (data.isFinished()) { if (vertices == null) { orderedVertices.addFirst(data.getFinishedData()); } } } } /* * Resets all VertexData objects. */ private void resetVertexData() { for (VertexData data : vertexToVertexData.values()) { data.setDiscovered(false); data.setFinished(false); } } /* * Lightweight class storing some data for every vertex. */ private static abstract class VertexData { private byte bitfield; private VertexData( boolean discovered, boolean finished) { this.bitfield = 0; setDiscovered(discovered); setFinished(finished); } private boolean isDiscovered() { if ((bitfield & 1) == 1) { return true; } return false; } private boolean isFinished() { if ((bitfield & 2) == 2) { return true; } return false; } private void setDiscovered(boolean discovered) { if (discovered) { bitfield |= 1; } else { bitfield &= ~1; } } private void setFinished(boolean finished) { if (finished) { bitfield |= 2; } else { bitfield &= ~2; } } abstract VertexData getFinishedData(); abstract V getVertex(); } private static final class VertexData1 extends VertexData { private final VertexData finishedData; private VertexData1( VertexData finishedData, boolean discovered, boolean finished) { super(discovered, finished); this.finishedData = finishedData; } @Override VertexData getFinishedData() { return finishedData; } @Override V getVertex() { return null; } } private static final class VertexData2 extends VertexData { private final V vertex; private VertexData2( V vertex, boolean discovered, boolean finished) { super(discovered, finished); this.vertex = vertex; } @Override VertexData getFinishedData() { return null; } @Override V getVertex() { return vertex; } } } // End StrongConnectivityInspector.java




© 2015 - 2024 Weber Informatics LLC | Privacy Policy