org.jgrapht.traverse.TopologicalOrderIterator 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-2008, by Barak Naveh and Contributors.
*
* 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.
*/
/* -----------------------------
* TopologicalOrderIterator.java
* -----------------------------
* (C) Copyright 2004-2008, by Marden Neubert and Contributors.
*
* Original Author: Marden Neubert
* Contributor(s): Barak Naveh, John V. Sichi
*
* $Id$
*
* Changes
* -------
* 17-Dec-2004 : Initial revision (MN);
* 25-Apr-2005 : Fixes for start vertex order (JVS);
* 06-Jun-2005 : Made generic (CH);
*
*/
package org.jgrapht.traverse;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.util.*;
/**
* Implements topological order traversal for a directed acyclic graph. A
* topological sort is a permutation p of the vertices of a graph such
* that an edge (i,j) implies that i appears before j
* in p (Skiena 1990, p. 208). See also
* http://mathworld.wolfram.com/TopologicalSort.html.
*
* See "Algorithms in Java, Third Edition, Part 5: Graph Algorithms" by
* Robert Sedgewick and "Data Structures and Algorithms with Object-Oriented
* Design Patterns in Java" by Bruno R. Preiss for implementation alternatives.
* The latter can be found online at
* http://www.brpreiss.com/books/opus5/
*
* For this iterator to work correctly the graph must be acyclic, and must
* not be modified during iteration. Currently there are no means to ensure
* that, nor to fail-fast; the results with cyclic input (including self-loops)
* or concurrent modifications are undefined. To precheck a graph for cycles,
* consider using {@link org.jgrapht.alg.CycleDetector} or {@link
* org.jgrapht.alg.StrongConnectivityInspector}.
*
* @author Marden Neubert
* @since Dec 18, 2004
*/
public class TopologicalOrderIterator
extends CrossComponentIterator
{
private Queue queue;
private Map inDegreeMap;
/**
* Creates a new topological order iterator over the directed graph
* specified, with arbitrary tie-breaking in case of partial order.
* Traversal will start at one of the graph's sources. See the
* definition of source at
* http://mathworld.wolfram.com/Source.html.
*
* @param dg the directed graph to be iterated.
*/
public TopologicalOrderIterator(DirectedGraph dg)
{
this(dg, new LinkedListQueue());
}
/**
* Creates a new topological order iterator over the directed graph
* specified, with a user-supplied queue implementation to allow customized
* control over tie-breaking in case of partial order. Traversal will start
* at one of the graph's sources. See the definition of source at
* http://mathworld.wolfram.com/Source.html.
*
* @param dg the directed graph to be iterated.
* @param queue queue to use for tie-break in case of partial order (e.g. a
* PriorityQueue can be used to break ties according to vertex priority);
* must be initially empty
*/
public TopologicalOrderIterator(DirectedGraph dg, Queue queue)
{
this(dg, queue, new HashMap());
}
// NOTE: This is a hack to deal with the fact that CrossComponentIterator
// needs to know the start vertex in its constructor
private TopologicalOrderIterator(
DirectedGraph dg,
Queue queue,
Map inDegreeMap)
{
this(dg, initialize(dg, queue, inDegreeMap));
this.queue = queue;
this.inDegreeMap = inDegreeMap;
// empty queue for non-empty graph would indicate presence of
// cycles (no roots found)
assert dg.vertexSet().isEmpty() || !queue.isEmpty();
}
// NOTE: This is intentionally private, because starting the sort "in the
// middle" doesn't make sense.
private TopologicalOrderIterator(DirectedGraph dg, V start)
{
super(dg, start);
}
/**
* @see CrossComponentIterator#isConnectedComponentExhausted()
*/
protected boolean isConnectedComponentExhausted()
{
// FIXME jvs 25-Apr-2005: This isn't correct for a graph with more than
// one component. We will actually exhaust a connected component
// before the queue is empty, because initialize adds roots from all
// components to the queue.
return queue.isEmpty();
}
/**
* @see CrossComponentIterator#encounterVertex(Object, Object)
*/
protected void encounterVertex(V vertex, E edge)
{
putSeenData(vertex, null);
decrementInDegree(vertex);
}
/**
* @see CrossComponentIterator#encounterVertexAgain(Object, Object)
*/
protected void encounterVertexAgain(V vertex, E edge)
{
decrementInDegree(vertex);
}
/**
* @see CrossComponentIterator#provideNextVertex()
*/
protected V provideNextVertex()
{
return queue.remove();
}
/**
* Decrements the in-degree of a vertex.
*
* @param vertex the vertex whose in-degree will be decremented.
*/
private void decrementInDegree(V vertex)
{
ModifiableInteger inDegree = inDegreeMap.get(vertex);
if (inDegree.value > 0) {
inDegree.value--;
if (inDegree.value == 0) {
queue.offer(vertex);
}
}
}
/**
* Initializes the internal traversal object structure. Sets up the internal
* queue with the directed graph vertices and creates the control structure
* for the in-degrees.
*
* @param dg the directed graph to be iterated.
* @param queue initializer for queue
* @param inDegreeMap initializer for inDegreeMap
*
* @return start vertex
*/
private static V initialize(
DirectedGraph dg,
Queue queue,
Map inDegreeMap)
{
for (Iterator i = dg.vertexSet().iterator(); i.hasNext();) {
V vertex = i.next();
int inDegree = dg.inDegreeOf(vertex);
inDegreeMap.put(vertex, new ModifiableInteger(inDegree));
if (inDegree == 0) {
queue.offer(vertex);
}
}
if (queue.isEmpty()) {
return null;
} else {
return queue.peek();
}
}
// NOTE jvs 22-Dec-2006: For JDK1.4-compatibility, we can't assume
// that LinkedList implements Queue, since that wasn't introduced
// until JDK1.5, so use an adapter here. Move this to
// top-level in org.jgrapht.util if anyone else needs it.
private static class LinkedListQueue
extends LinkedList
implements Queue
{
private static final long serialVersionUID = 4217659843476891334L;
public T element()
{
return getFirst();
}
public boolean offer(T o)
{
return add(o);
}
public T peek()
{
if (isEmpty()) {
return null;
}
return getFirst();
}
public T poll()
{
if (isEmpty()) {
return null;
}
return removeFirst();
}
public T remove()
{
return removeFirst();
}
}
}
// End TopologicalOrderIterator.java