org.jgrapht.alg.connectivity.ConnectivityInspector Maven / Gradle / Ivy
The newest version!
/*
* (C) Copyright 2003-2023, by Barak Naveh and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.alg.connectivity;
import org.jgrapht.*;
import org.jgrapht.event.*;
import org.jgrapht.graph.*;
import org.jgrapht.traverse.*;
import org.jgrapht.util.*;
import java.util.*;
/**
* Allows obtaining various connectivity aspects of a graph. The inspected graph is specified
* at construction time and cannot be modified. Currently, the inspector supports connected
* components for an undirected graph and weakly connected components for a directed graph. To find
* strongly connected components, use {@link KosarajuStrongConnectivityInspector} instead.
*
*
* The inspector methods work in a lazy fashion: no computation is performed unless immediately
* necessary. Computation are done once and results and cached within this class for future need.
*
*
*
* The inspector is also a {@link org.jgrapht.event.GraphListener}. If added as a listener to the
* inspected graph, the inspector will amend internal cached results instead of recomputing them. It
* is efficient when a few modifications are applied to a large graph. If many modifications are
* expected it will not be efficient due to added overhead on graph update operations. If inspector
* is added as listener to a graph other than the one it inspects, results are undefined.
*
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Barak Naveh
* @author John V. Sichi
*/
public class ConnectivityInspector
implements GraphListener
{
private List> connectedSets;
private Map> vertexToConnectedSet;
private Graph graph;
/**
* Creates a connectivity inspector for the specified graph.
*
* @param g the graph for which a connectivity inspector to be created.
*/
public ConnectivityInspector(Graph g)
{
init();
this.graph = Objects.requireNonNull(g);
if (g.getType().isDirected())
this.graph = new AsUndirectedGraph<>(g);
}
/**
* Test if the inspected graph is connected. A graph is connected when there is a path between
* every pair of vertices. In a connected graph, there are no unreachable vertices. When the
* inspected graph is a directed graph, this method returns true if and only if the
* inspected graph is weakly connected. An empty graph is not considered
* connected.
*
* @return true
if and only if inspected graph is connected.
*/
public boolean isConnected()
{
return lazyFindConnectedSets().size() == 1;
}
/**
* Returns a set of all vertices that are in the maximally connected component together with the
* specified vertex. For more on maximally connected component, see
*
* http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html.
*
* @param vertex the vertex for which the connected set to be returned.
*
* @return a set of all vertices that are in the maximally connected component together with the
* specified vertex.
*/
public Set connectedSetOf(V vertex)
{
Set connectedSet = vertexToConnectedSet.get(vertex);
if (connectedSet == null) {
connectedSet = new HashSet<>();
BreadthFirstIterator i = new BreadthFirstIterator<>(graph, vertex);
while (i.hasNext()) {
connectedSet.add(i.next());
}
vertexToConnectedSet.put(vertex, connectedSet);
}
return connectedSet;
}
/**
* Returns a list of Set
s, where each set contains all vertices that are in the
* same maximally connected component. All graph vertices occur in exactly one set. For more on
* maximally connected component, see
*
* http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html.
*
* @return Returns a list of Set
s, where each set contains all vertices that are
* in the same maximally connected component.
*/
public List> connectedSets()
{
return lazyFindConnectedSets();
}
/**
* @see GraphListener#edgeAdded(GraphEdgeChangeEvent)
*/
@Override
public void edgeAdded(GraphEdgeChangeEvent e)
{
V source = e.getEdgeSource();
V target = e.getEdgeTarget();
Set sourceSet = connectedSetOf(source);
Set targetSet = connectedSetOf(target);
// If source and target are in the same set, do nothing, otherwise, merge sets
if (sourceSet != targetSet) {
Set merge =
CollectionUtil.newHashSetWithExpectedSize(sourceSet.size() + targetSet.size());
merge.addAll(sourceSet);
merge.addAll(targetSet);
connectedSets.remove(sourceSet);
connectedSets.remove(targetSet);
connectedSets.add(merge);
for (V v : merge)
vertexToConnectedSet.put(v, merge);
}
}
/**
* @see GraphListener#edgeRemoved(GraphEdgeChangeEvent)
*/
@Override
public void edgeRemoved(GraphEdgeChangeEvent e)
{
init(); // for now invalidate cached results, in the future need to
// amend them. If the edge is a bridge, 2 components need to be split.
}
/**
* Tests whether two vertices lay respectively in the same connected component (undirected
* graph), or in the same weakly connected component (directed graph).
*
* @param sourceVertex one end of the path.
* @param targetVertex another end of the path.
*
* @return true
if and only if the source and target vertex are in the same
* connected component (undirected graph), or in the same weakly connected component
* (directed graph).
*/
public boolean pathExists(V sourceVertex, V targetVertex)
{
return connectedSetOf(sourceVertex).contains(targetVertex);
}
/**
* @see VertexSetListener#vertexAdded(GraphVertexChangeEvent)
*/
@Override
public void vertexAdded(GraphVertexChangeEvent e)
{
Set component = new HashSet<>();
component.add(e.getVertex());
connectedSets.add(component);
vertexToConnectedSet.put(e.getVertex(), component);
}
/**
* @see VertexSetListener#vertexRemoved(GraphVertexChangeEvent)
*/
@Override
public void vertexRemoved(GraphVertexChangeEvent e)
{
init(); // for now invalidate cached results, in the future need to
// amend them. If the vertex is an articulation point, two
// components need to be split
}
private void init()
{
connectedSets = null;
vertexToConnectedSet = new HashMap<>();
}
private List> lazyFindConnectedSets()
{
if (connectedSets == null) {
connectedSets = new ArrayList<>();
Set vertexSet = graph.vertexSet();
if (!vertexSet.isEmpty()) {
BreadthFirstIterator i = new BreadthFirstIterator<>(graph);
i.addTraversalListener(new MyTraversalListener());
while (i.hasNext()) {
i.next();
}
}
}
return connectedSets;
}
/**
* A traversal listener that groups all vertices according to to their containing connected set.
*
* @author Barak Naveh
*/
private class MyTraversalListener
extends TraversalListenerAdapter
{
private Set currentConnectedSet;
/**
* @see TraversalListenerAdapter#connectedComponentFinished(ConnectedComponentTraversalEvent)
*/
@Override
public void connectedComponentFinished(ConnectedComponentTraversalEvent e)
{
connectedSets.add(currentConnectedSet);
}
/**
* @see TraversalListenerAdapter#connectedComponentStarted(ConnectedComponentTraversalEvent)
*/
@Override
public void connectedComponentStarted(ConnectedComponentTraversalEvent e)
{
currentConnectedSet = new HashSet<>();
}
/**
* @see TraversalListenerAdapter#vertexTraversed(VertexTraversalEvent)
*/
@Override
public void vertexTraversed(VertexTraversalEvent e)
{
V v = e.getVertex();
currentConnectedSet.add(v);
vertexToConnectedSet.put(v, currentConnectedSet);
}
}
}