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-2007, by Barak Naveh and Contributors.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
/* --------------------------
* StrongConnectivityInspector.java
* --------------------------
* (C) Copyright 2005-2007, by Christian Soltenborn and Contributors.
*
* Original Author: Christian Soltenborn
*
* $Id: StrongConnectivityInspector.java 568 2007-09-30 00:12:18Z perfecthash $
*
* Changes
* -------
* 2-Feb-2005 : Initial revision (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
{
//~ Instance fields --------------------------------------------------------
// 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;
//~ Constructors -----------------------------------------------------------
/**
* 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;
}
//~ Methods ----------------------------------------------------------------
/**
* 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
Iterator> iter =
vertexToVertexData.values().iterator();
while (iter.hasNext()) {
VertexData data = iter.next();
if (!data.discovered) {
dfsVisit(graph, data, null);
}
}
// calculate inverse graph (i.e. every edge is reversed)
DirectedGraph inverseGraph =
new DefaultDirectedGraph(graph.getEdgeFactory());
Graphs.addGraphReversed(inverseGraph, 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
iter = orderedVertices.iterator();
while (iter.hasNext()) {
VertexData data = iter.next();
if (!data.discovered) {
// 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());
Iterator> iter = sets.iterator();
while (iter.hasNext()) {
stronglyConnectedSubgraphs.add(
new DirectedSubgraph(
graph,
iter.next(),
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());
Iterator iter = graph.vertexSet().iterator();
while (iter.hasNext()) {
V vertex = iter.next();
vertexToVertexData.put(
vertex,
new VertexData(null, 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)
{
Stack> stack = new Stack>();
stack.push(vertexData);
while (!stack.isEmpty()) {
VertexData data = stack.pop();
if (!data.discovered) {
data.discovered = true;
if (vertices != null) {
vertices.add(data.vertex);
}
stack.push(new VertexData(data, null, true, true));
// follow all edges
Iterator extends E> iter =
visitedGraph.outgoingEdgesOf(data.vertex).iterator();
while (iter.hasNext()) {
E edge = iter.next();
VertexData targetData =
vertexToVertexData.get(
visitedGraph.getEdgeTarget(edge));
if (!targetData.discovered) {
// the "recursion"
stack.push(targetData);
}
}
} else if (data.finished) {
if (vertices == null) {
orderedVertices.addFirst(data.finishedData);
}
}
}
}
/*
* Resets all VertexData objects.
*/
private void resetVertexData()
{
Iterator> iter = vertexToVertexData.values().iterator();
while (iter.hasNext()) {
VertexData data = iter.next();
data.discovered = false;
data.finished = false;
}
}
//~ Inner Classes ----------------------------------------------------------
/*
* Lightweight class storing some data for every vertex.
*/
private static final class VertexData
{
// TODO jvs 24-June-2006: more compact representation;
// I added finishedData to clean up the generics warnings
private final VertexData finishedData;
private final V vertex;
private boolean discovered;
private boolean finished;
private VertexData(
VertexData finishedData,
V vertex,
boolean discovered,
boolean finished)
{
this.finishedData = finishedData;
this.vertex = vertex;
this.discovered = discovered;
this.finished = finished;
}
}
}
// End StrongConnectivityInspector.java