directedacyclicgraph.java
来自「JAVA图论的算法包。用过觉得还不错」· Java 代码 · 共 1,200 行 · 第 1/3 页
JAVA
1,200 行
/* ==========================================
* 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 library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
/* -------------------
* DirectedAcyclicGraph.java
* -------------------
* (C) Copyright 2008-2008, by Peter Giles and Contributors.
*
* Original Author: Peter Giles
* Contributor(s): John V. Sichi
*
* $Id: DirectedAcyclicGraph.java 637 2008-09-28 22:23:11Z perfecthash $
*
* Changes
* -------
* 17-Mar-2008 : Initial revision (PG);
* 23-Aug-2008 : Added VisitedBitSetImpl and made it the default (JVS);
*
*/
package org.jgrapht.experimental.dag;
import java.io.*;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.graph.*;
/**
* <p>DirectedAcyclicGraph implements a DAG that can be modified (vertices &
* edges added and removed), is guaranteed to remain acyclic, and provides fast
* topological order iteration.</p>
*
* <p>This is done using a dynamic topological sort which is based on the
* algorithm PK described in "D. Pearce & P. Kelly, 2007: A Dynamic
* Topological Sort Algorithm for Directed Acyclic Graphs", (see <a
* href="http://www.mcs.vuw.ac.nz/~djp/files/PK-JEA07.pdf">Paper</a> or <a
* href="http://doi.acm.org/10.1145/1187436.1210590">ACM link</a> for details).
* </p>
*
* <p>The implementation differs from the algorithm specified in the above paper
* in some ways, perhaps most notably in that the topological ordering is stored
* by default using two HashMaps, which will have some effects on runtime, but
* also allows for vertex addition and removal, and other operations which are
* helpful for manipulating or combining DAGs. This storage mechanism is
* pluggable for subclassers.</p>
*
* <p>This class makes no claims to thread safety, and concurrent usage from
* multiple threads will produce undefined results.</p>
*
* @author Peter Giles, gilesp@u.washington.edu
*/
public class DirectedAcyclicGraph<V, E>
extends SimpleDirectedGraph<V, E>
{
//~ Static fields/initializers ---------------------------------------------
private static final long serialVersionUID = 4522128427004938150L;
//~ Instance fields --------------------------------------------------------
private TopoComparator<V> topoComparator;
private TopoOrderMapping<V> topoOrderMap;
private int maxTopoIndex = 0;
private int minTopoIndex = 0;
// this update count is used to keep internal topological iterators honest
private long topologyUpdateCount = 0;
/**
* Pluggable VisitedFactory implementation
*/
private VisitedFactory visitedFactory = new VisitedBitSetImpl();
/**
* Pluggable TopoOrderMappingFactory implementation
*/
private TopoOrderMappingFactory<V> topoOrderFactory = new TopoVertexBiMap();
//~ Constructors -----------------------------------------------------------
public DirectedAcyclicGraph(Class<? extends E> arg0)
{
super(arg0);
initialize();
}
DirectedAcyclicGraph(
Class<? extends E> arg0,
VisitedFactory visitedFactory,
TopoOrderMappingFactory<V> topoOrderFactory)
{
super(arg0);
if (visitedFactory != null) {
this.visitedFactory = visitedFactory;
}
if (topoOrderFactory != null) {
this.topoOrderFactory = topoOrderFactory;
}
initialize();
}
//~ Methods ----------------------------------------------------------------
/**
* set the topoOrderMap based on the current factory, and create the
* comparator;
*/
private void initialize()
{
topoOrderMap = topoOrderFactory.getTopoOrderMapping();
topoComparator = new TopoComparator<V>(topoOrderMap);
}
/**
* iterator will traverse the vertices in topological order, meaning that
* for a directed graph G = (V,E), if there exists a path from vertex va to
* vertex vb then va is guaranteed to come before vertex vb in the iteration
* order.
*
* @return an iterator that will traverse the graph in topological order
*/
public Iterator<V> iterator()
{
return new TopoIterator();
}
/**
* adds the vertex if it wasn't already in the graph, and puts it at the top
* of the internal topological vertex ordering
*/
@Override public boolean addVertex(V v)
{
boolean added = super.addVertex(v);
if (added) {
// add to the top
++maxTopoIndex;
topoOrderMap.putVertex(maxTopoIndex, v);
++topologyUpdateCount;
}
return added;
}
/**
* adds the vertex if it wasn't already in the graph, and puts it either at
* the top or the bottom of the topological ordering, depending on the value
* of addToTop. This may provide useful optimizations for merging
* DirectedAcyclicGraphS that become connected.
*
* @param v
* @param addToTop
*
* @return
*/
public boolean addVertex(V v, boolean addToTop)
{
boolean added = super.addVertex(v);
if (added) {
int insertIndex;
// add to the top
if (addToTop) {
insertIndex = ++maxTopoIndex;
} else {
insertIndex = --minTopoIndex;
}
topoOrderMap.putVertex(insertIndex, v);
++topologyUpdateCount;
}
return added;
}
/**
* <p>Adds the given edge and updates the internal topological order for
* consistency IFF
*
* <UL>
* <li>there is not already an edge (fromVertex, toVertex) in the graph
* <li>the edge does not induce a cycle in the graph
* </ul>
* </p>
*
* @return null if the edge is already in the graph, else the created edge
* is returned
*
* @throws IllegalArgumentException If either fromVertex or toVertex is not
* a member of the graph
* @throws CycleFoundException if the edge would induce a cycle in the graph
*
* @see Graph#addEdge(Object, Object, Object)
*/
public E addDagEdge(V fromVertex, V toVertex)
throws CycleFoundException
{
Integer lb = topoOrderMap.getTopologicalIndex(toVertex);
Integer ub = topoOrderMap.getTopologicalIndex(fromVertex);
if ((lb == null) || (ub == null)) {
throw new IllegalArgumentException(
"vertices must be in the graph already!");
}
if (lb < ub) {
Set<V> df = new HashSet<V>();
Set<V> db = new HashSet<V>();
// Discovery
Region affectedRegion = new Region(lb, ub);
Visited visited = visitedFactory.getInstance(affectedRegion);
// throws CycleFoundException if there is a cycle
dfsF(toVertex, df, visited, affectedRegion);
dfsB(fromVertex, db, visited, affectedRegion);
reorder(df, db, visited);
++topologyUpdateCount; // if we do a reorder, than the topology has
// been updated
}
return super.addEdge(fromVertex, toVertex);
}
/**
* identical to {@link #addDagEdge(Object, Object)}, except an unchecked
* {@link IllegalArgumentException} is thrown if a cycle would have been
* induced by this edge
*/
@Override public E addEdge(V sourceVertex, V targetVertex)
{
E result = null;
try {
result = addDagEdge(sourceVertex, targetVertex);
} catch (CycleFoundException e) {
throw new IllegalArgumentException(e);
}
return result;
}
/**
* <p>Adds the given edge and updates the internal topological order for
* consistency IFF
*
* <UL>
* <li>the given edge is not already a member of the graph
* <li>there is not already an edge (fromVertex, toVertex) in the graph
* <li>the edge does not induce a cycle in the graph
* </ul>
* </p>
*
* @return true if the edge was added to the graph
*
* @throws CycleFoundException if adding an edge (fromVertex, toVertex) to
* the graph would induce a cycle.
*
* @see Graph#addEdge(Object, Object, Object)
*/
public boolean addDagEdge(V fromVertex, V toVertex, E e)
throws CycleFoundException
{
if (e == null) {
throw new NullPointerException();
} else if (containsEdge(e)) {
return false;
}
Integer lb = topoOrderMap.getTopologicalIndex(toVertex);
Integer ub = topoOrderMap.getTopologicalIndex(fromVertex);
if ((lb == null) || (ub == null)) {
throw new IllegalArgumentException(
"vertices must be in the graph already!");
}
if (lb < ub) {
Set<V> df = new HashSet<V>();
Set<V> db = new HashSet<V>();
// Discovery
Region affectedRegion = new Region(lb, ub);
Visited visited = visitedFactory.getInstance(affectedRegion);
// throws CycleFoundException if there is a cycle
dfsF(toVertex, df, visited, affectedRegion);
dfsB(fromVertex, db, visited, affectedRegion);
reorder(df, db, visited);
++topologyUpdateCount; // if we do a reorder, than the topology has
// been updated
}
return super.addEdge(fromVertex, toVertex, e);
}
/**
* identical to {@link #addDagEdge(Object, Object, Object)}, except an
* unchecked {@link IllegalArgumentException} is thrown if a cycle would
* have been induced by this edge
*/
@Override public boolean addEdge(V sourceVertex, V targetVertex, E edge)
{
boolean result;
try {
result = addDagEdge(sourceVertex, targetVertex, edge);
} catch (CycleFoundException e) {
throw new IllegalArgumentException(e);
}
return result;
}
// note that this can leave holes in the topological ordering, which
// (depending on the TopoOrderMap implementation) can degrade performance
// for certain operations over time
@Override public boolean removeVertex(V v)
{
boolean removed = super.removeVertex(v);
if (removed) {
Integer topoIndex = topoOrderMap.removeVertex(v);
// contract minTopoIndex as we are able
if (topoIndex == minTopoIndex) {
while (
(minTopoIndex < 0)
&& (null == topoOrderMap.getVertex(minTopoIndex)))
{
++minTopoIndex;
}
}
// contract maxTopoIndex as we are able
if (topoIndex == maxTopoIndex) {
while (
(maxTopoIndex > 0)
&& (null == topoOrderMap.getVertex(maxTopoIndex)))
{
--maxTopoIndex;
}
}
++topologyUpdateCount;
}
return removed;
}
@Override public boolean removeAllVertices(Collection<? extends V> arg0)
{
boolean removed = super.removeAllVertices(arg0);
topoOrderMap.removeAllVertices();
maxTopoIndex = 0;
minTopoIndex = 0;
++topologyUpdateCount;
return removed;
}
/**
* Depth first search forward, building up the set (df) of forward-connected
* vertices in the Affected Region
*
* @param vertex the vertex being visited
* @param df the set we are populating with forward connected vertices in
* the Affected Region
* @param visited a simple data structure that lets us know if we already
* visited a node with a given topo index
* @param topoIndexMap for quick lookups, a map from vertex to topo index in
* the AR
* @param ub the topo index of the original fromVertex -- used for cycle
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?