com.salesforce.jgrapht.alg.KosarajuStrongConnectivityInspector Maven / Gradle / Ivy
Show all versions of AptSpringProcessor Show documentation
/*
* (C) Copyright 2005-2017, by Christian Soltenborn and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* 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 com.salesforce.jgrapht.alg;
import java.util.*;
import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.graph.*;
/**
*
* Complements the {@link com.salesforce.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 com.salesforce.jgrapht.alg.ConnectivityInspector}, this class does not implement incremental
* inspection. The full algorithm is executed at the first call of
* {@link KosarajuStrongConnectivityInspector#stronglyConnectedSets()} or
* {@link KosarajuStrongConnectivityInspector#isStronglyConnected()}.
*
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Christian Soltenborn
* @author Christian Hammer
* @since Feb 2, 2005
*/
public class KosarajuStrongConnectivityInspector
implements StrongConnectivityAlgorithm
{
// 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 StrongConnectivityAlgorithm class.
*
* @param directedGraph the graph to inspect
*
* @throws IllegalArgumentException if the input graph is null
*/
public KosarajuStrongConnectivityInspector(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 StrongConnectivityAlgorithm.
*
* @return the graph inspected by this StrongConnectivityAlgorithm
*/
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 KosarajuStrongConnectivityInspector#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()
{
return (bitfield & 1) == 1;
}
private boolean isFinished()
{
return (bitfield & 2) == 2;
}
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 StrongConnectivityAlgorithm.java