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

org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2005-2023, by Christian Soltenborn 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.graph.*;
import org.jgrapht.util.*;

import java.util.*;

/**
 * Computes strongly connected components of a directed graph. The algorithm is implemented after
 * "Cormen et al: Introduction to algorithms", Chapter 22.5. It has a running time of $O(V + E)$.
 *
 * 

* Unlike {@link 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 */ public class KosarajuStrongConnectivityInspector extends AbstractStrongConnectivityInspector { // stores the vertices, ordered by their finishing time in first dfs private LinkedList> orderedVertices; // maps vertices to their VertexData object private Map> vertexToVertexData; /** * Constructor * * @param graph the input graph * @throws NullPointerException if the input graph is null */ public KosarajuStrongConnectivityInspector(Graph graph) { super(graph); } @Override public List> stronglyConnectedSets() { if (stronglyConnectedSets == null) { orderedVertices = new LinkedList<>(); stronglyConnectedSets = new ArrayList<>(); // 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) Graph 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; } /* * Creates a VertexData object for every vertex in the graph and stores them in a HashMap. */ private void createVertexData() { vertexToVertexData = CollectionUtil.newHashMapWithExpectedSize(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(Graph 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() && 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 abstract static 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; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy