You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.7 KiB
Java

package de.normalisiert.utils.graphs;
import java.util.List;
import java.util.Vector;
/**
* Searchs all elementary cycles in a given directed graph. The implementation
* is independent from the concrete objects that represent the graphnodes, it
* just needs an array of the objects representing the nodes the graph
* and an adjacency-matrix of type boolean, representing the edges of the
* graph. It then calculates based on the adjacency-matrix the elementary
* cycles and returns a list, which contains lists itself with the objects of the
* concrete graphnodes-implementation. Each of these lists represents an
* elementary cycle.<br><br>
*
* The implementation uses the algorithm of Donald B. Johnson for the search of
* the elementary cycles. For a description of the algorithm see:<br>
* Donald B. Johnson: Finding All the Elementary Circuits of a Directed Graph.
* SIAM Journal on Computing. Volumne 4, Nr. 1 (1975), pp. 77-84.<br><br>
*
* The algorithm of Johnson is based on the search for strong connected
* components in a graph. For a description of this part see:<br>
* Robert Tarjan: Depth-first search and linear graph algorithms. In: SIAM
* Journal on Computing. Volume 1, Nr. 2 (1972), pp. 146-160.<br>
*
* @author Frank Meyer, web_at_normalisiert_dot_de
* @version 1.2, 22.03.2009
*
*/
public class ElementaryCyclesSearch {
/** List of cycles */
private List cycles = null;
/** Adjacency-list of graph */
private int[][] adjList = null;
/** Graphnodes */
private Object[] graphNodes = null;
/** Blocked nodes, used by the algorithm of Johnson */
private boolean[] blocked = null;
/** B-Lists, used by the algorithm of Johnson */
private Vector[] B = null;
/** Stack for nodes, used by the algorithm of Johnson */
private Vector stack = null;
/**
* Constructor.
*
* @param matrix adjacency-matrix of the graph
* @param graphNodes array of the graphnodes of the graph; this is used to
* build sets of the elementary cycles containing the objects of the original
* graph-representation
*/
public ElementaryCyclesSearch(boolean[][] matrix, Object[] graphNodes) {
this.graphNodes = graphNodes;
this.adjList = AdjacencyList.getAdjacencyList(matrix);
}
/**
* Returns List::List::Object with the Lists of nodes of all elementary
* cycles in the graph.
*
* @return List::List::Object with the Lists of the elementary cycles.
*/
public List getElementaryCycles() {
this.cycles = new Vector();
this.blocked = new boolean[this.adjList.length];
this.B = new Vector[this.adjList.length];
this.stack = new Vector();
StrongConnectedComponents sccs = new StrongConnectedComponents(this.adjList);
int s = 0;
while (true) {
SCCResult sccResult = sccs.getAdjacencyList(s);
if (sccResult != null && sccResult.getAdjList() != null) {
Vector[] scc = sccResult.getAdjList();
s = sccResult.getLowestNodeId();
for (int j = 0; j < scc.length; j++) {
if ((scc[j] != null) && (scc[j].size() > 0)) {
this.blocked[j] = false;
this.B[j] = new Vector();
}
}
this.findCycles(s, s, scc);
s++;
} else {
break;
}
}
return this.cycles;
}
/**
* Calculates the cycles containing a given node in a strongly connected
* component. The method calls itself recursivly.
*
* @param v
* @param s
* @param adjList adjacency-list with the subgraph of the strongly
* connected component s is part of.
* @return true, if cycle found; false otherwise
*/
private boolean findCycles(int v, int s, Vector[] adjList) {
boolean f = false;
this.stack.add(new Integer(v));
this.blocked[v] = true;
for (int i = 0; i < adjList[v].size(); i++) {
int w = ((Integer) adjList[v].get(i)).intValue();
// found cycle
if (w == s) {
Vector cycle = new Vector();
for (int j = 0; j < this.stack.size(); j++) {
int index = ((Integer) this.stack.get(j)).intValue();
cycle.add(this.graphNodes[index]);
}
this.cycles.add(cycle);
f = true;
} else if (!this.blocked[w]) {
if (this.findCycles(w, s, adjList)) {
f = true;
}
}
}
if (f) {
this.unblock(v);
} else {
for (int i = 0; i < adjList[v].size(); i++) {
int w = ((Integer) adjList[v].get(i)).intValue();
if (!this.B[w].contains(new Integer(v))) {
this.B[w].add(new Integer(v));
}
}
}
this.stack.remove(new Integer(v));
return f;
}
/**
* Unblocks recursivly all blocked nodes, starting with a given node.
*
* @param node node to unblock
*/
private void unblock(int node) {
this.blocked[node] = false;
Vector Bnode = this.B[node];
while (Bnode.size() > 0) {
Integer w = (Integer) Bnode.get(0);
Bnode.remove(0);
if (this.blocked[w.intValue()]) {
this.unblock(w.intValue());
}
}
}
}