org.jgrapht.traverse.TopologicalOrderIterator Maven / Gradle / Ivy
/*
* (C) Copyright 2004-2023, by Marden Neubert 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.traverse;
import org.jgrapht.*;
import org.jgrapht.util.*;
import java.util.*;
/**
* A topological ordering iterator for a directed acyclic graph.
*
*
* A topological order is a permutation p
of the vertices of a graph such that an edge
* (i,j)
implies that i
appears before j
in p
.
* For more information see
* wikipedia or
* wolfram.
*
*
* The iterator crosses components but does not track them, it only tracks visited vertices. The
* iterator will detect (at some point) if the graph is not a directed acyclic graph and throw a
* {@link NotDirectedAcyclicGraphException}.
*
*
* For this iterator to work correctly the graph must not be modified during iteration. Currently
* there are no means to ensure that, nor to fail-fast. The results of such modifications are
* undefined.
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Marden Neubert
* @author Dimitrios Michail
*/
public class TopologicalOrderIterator
extends AbstractGraphIterator
{
private Queue queue;
private Map inDegreeMap;
private int remainingVertices;
private V cur;
/**
* Construct a topological order iterator.
*
*
* Traversal will start at one of the graph's sources. See the definition of source at
*
* http://mathworld.wolfram.com/Source.html. In case of partial order, tie-breaking is
* arbitrary.
*
* @param graph the directed graph to be iterated
*/
public TopologicalOrderIterator(Graph graph)
{
this(graph, (Comparator) null);
}
/**
* Construct a topological order iterator.
*
*
* Traversal will start at one of the graph's sources. See the definition of source at
*
* http://mathworld.wolfram.com/Source.html. In case of partial order, a comparator is used
* to break ties.
*
* @param graph the directed graph to be iterated
* @param comparator comparator in order to break ties in case of partial order
*/
public TopologicalOrderIterator(Graph graph, Comparator comparator)
{
super(graph);
GraphTests.requireDirected(graph);
// create queue
if (comparator == null) {
this.queue = new ArrayDeque<>();
} else {
this.queue = new PriorityQueue<>(comparator);
}
// count in-degrees
this.inDegreeMap = new HashMap<>();
for (V v : graph.vertexSet()) {
int d = 0;
for (E e : graph.incomingEdgesOf(v)) {
V u = Graphs.getOppositeVertex(graph, e, v);
if (v.equals(u)) {
throw new NotDirectedAcyclicGraphException();
}
d++;
}
inDegreeMap.put(v, new ModifiableInteger(d));
if (d == 0) {
queue.offer(v);
}
}
// record vertices count
this.remainingVertices = graph.vertexSet().size();
}
/**
* {@inheritDoc}
*
* Always returns true since the iterator does not care about components.
*/
@Override
public boolean isCrossComponentTraversal()
{
return true;
}
/**
* {@inheritDoc}
*
* Trying to disable the cross components nature of this iterator will result into throwing a
* {@link IllegalArgumentException}.
*/
@Override
public void setCrossComponentTraversal(boolean crossComponentTraversal)
{
if (!crossComponentTraversal) {
throw new IllegalArgumentException("Iterator is always cross-component");
}
}
@Override
public boolean hasNext()
{
if (cur != null) {
return true;
}
cur = advance();
if (cur != null && nListeners != 0) {
fireVertexTraversed(createVertexTraversalEvent(cur));
}
return cur != null;
}
@Override
public V next()
{
if (!hasNext()) {
throw new NoSuchElementException();
}
V result = cur;
cur = null;
if (nListeners != 0) {
fireVertexFinished(createVertexTraversalEvent(result));
}
return result;
}
private V advance()
{
V result = queue.poll();
if (result != null) {
for (E e : graph.outgoingEdgesOf(result)) {
V other = Graphs.getOppositeVertex(graph, e, result);
ModifiableInteger inDegree = inDegreeMap.get(other);
if (inDegree.value > 0) {
inDegree.value--;
if (inDegree.value == 0) {
queue.offer(other);
}
}
}
--remainingVertices;
} else {
/*
* Still expecting some vertices, but no vertex has zero degree.
*/
if (remainingVertices > 0) {
throw new NotDirectedAcyclicGraphException();
}
}
return result;
}
}