Graph Implemented
- added a new method in graph interface - added method in the test - graph implemented and pass all the tests
This commit is contained in:
@@ -228,6 +228,17 @@ public interface Graph<V, W extends Number> extends Iterable<V> {
|
|||||||
*/
|
*/
|
||||||
Set<Edge<V, W>> edges();
|
Set<Edge<V, W>> edges();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all the edges from a particular vertex.<br>
|
||||||
|
* Note: the edges that is returned are the edges that goes IN this vertex AND the edges that goes OUT of it.
|
||||||
|
*
|
||||||
|
* @param vertex a vertex of the graph
|
||||||
|
* @return a set of edges
|
||||||
|
* @throws NullPointerException if the vertex is null
|
||||||
|
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||||
|
*/
|
||||||
|
Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the vertices that are children of the vertex passed as parameter.<br>
|
* Get all the vertices that are children of the vertex passed as parameter.<br>
|
||||||
* The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge
|
* The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge
|
||||||
|
|||||||
384
src/berack96/sim/util/graph/MapGraph.java
Normal file
384
src/berack96/sim/util/graph/MapGraph.java
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
package berack96.sim.util.graph;
|
||||||
|
|
||||||
|
import berack96.sim.util.graph.visit.Dijkstra;
|
||||||
|
import berack96.sim.util.graph.visit.Tarjan;
|
||||||
|
import berack96.sim.util.graph.visit.VisitStrategy;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Graph that uses HashMap for vertices and edges<br>
|
||||||
|
* More specifically it utilizes a Map containing all the vertices mapped to all their edges<br>
|
||||||
|
* Technically this version of the graph combine the fast adding/removing of the edges of the Matrix implementation,
|
||||||
|
* with the low memory and fast adding/removing of vertices of the Linked List implementation.<br>
|
||||||
|
* This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n)
|
||||||
|
*
|
||||||
|
* @param <V> the vertices
|
||||||
|
* @param <W> the weight of the edges
|
||||||
|
*/
|
||||||
|
public class MapGraph<V, W extends Number> implements Graph<V, W> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map that contains the edges from a vertex to another<br>
|
||||||
|
* The first vertex is the vertex where start the edge, the second one is where the edge goes<br>
|
||||||
|
* If an edge exist, then it's weight is returned
|
||||||
|
*/
|
||||||
|
private final Map<V, Map<V, W>> edges = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change
|
||||||
|
*/
|
||||||
|
private Tarjan<V, W> tarjan = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change
|
||||||
|
*/
|
||||||
|
private Map<V, Dijkstra<V, W>> dijkstra = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCyclic() {
|
||||||
|
return stronglyConnectedComponents().size() != numberOfVertices();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDAG() {
|
||||||
|
return !isCyclic();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVertex(V vertex) throws NullPointerException {
|
||||||
|
checkNull(vertex);
|
||||||
|
graphChanged();
|
||||||
|
edges.put(vertex, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
|
||||||
|
if (contains(vertex))
|
||||||
|
return false;
|
||||||
|
addVertex(vertex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAllVertices(Set<V> vertices) throws NullPointerException {
|
||||||
|
checkNull(vertices);
|
||||||
|
vertices.forEach(this::addVertexIfAbsent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
graphChanged();
|
||||||
|
edges.remove(vertex);
|
||||||
|
edges.forEach((v, map) -> map.remove(vertex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllVertex() {
|
||||||
|
graphChanged();
|
||||||
|
edges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(V vertex) throws NullPointerException {
|
||||||
|
checkNull(vertex);
|
||||||
|
return edges.containsKey(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex1);
|
||||||
|
checkNullAndExist(vertex2);
|
||||||
|
checkNull(weight);
|
||||||
|
|
||||||
|
graphChanged();
|
||||||
|
return edges.get(vertex1).put(vertex2, weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
|
||||||
|
addVertexIfAbsent(vertex1);
|
||||||
|
addVertexIfAbsent(vertex2);
|
||||||
|
return addEdge(vertex1, vertex2, weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAllEdges(Set<Edge<V, W>> edges) throws NullPointerException {
|
||||||
|
edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex1);
|
||||||
|
checkNullAndExist(vertex2);
|
||||||
|
|
||||||
|
return edges.get(vertex1).get(vertex2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex1);
|
||||||
|
checkNullAndExist(vertex2);
|
||||||
|
|
||||||
|
graphChanged();
|
||||||
|
edges.get(vertex1).remove(vertex2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
graphChanged();
|
||||||
|
edges.forEach((v, map) -> map.remove(vertex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
graphChanged();
|
||||||
|
edges.put(vertex, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
removeVertex(vertex);
|
||||||
|
addVertex(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllEdge() {
|
||||||
|
graphChanged();
|
||||||
|
edges.forEach((v, map) -> map.clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex1);
|
||||||
|
checkNullAndExist(vertex2);
|
||||||
|
|
||||||
|
return edges.get(vertex1).get(vertex2) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> vertices() {
|
||||||
|
return new HashSet<>(edges.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Edge<V, W>> edges() {
|
||||||
|
Set<Edge<V, W>> allEdges = new HashSet<>();
|
||||||
|
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
|
||||||
|
return allEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
Set<Edge<V, W>> set = new HashSet<>();
|
||||||
|
edges.forEach((source, map) -> map.forEach((destination, weight) -> {
|
||||||
|
if (destination.equals(vertex) || source.equals(vertex))
|
||||||
|
set.add(new Edge<>(source, destination, weight));
|
||||||
|
}));
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
return new HashSet<>(edges.get(vertex).keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<V, W> getChildrenAndWeight(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
return new HashMap<>(edges.get(vertex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
Set<V> set = new HashSet<>();
|
||||||
|
edges.forEach((v, map) -> {
|
||||||
|
if (map.containsKey(vertex)) set.add(v);
|
||||||
|
});
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
AtomicInteger sum = new AtomicInteger();
|
||||||
|
edges.forEach((v, map) -> {
|
||||||
|
if (map.containsKey(vertex))
|
||||||
|
sum.getAndIncrement();
|
||||||
|
});
|
||||||
|
|
||||||
|
return sum.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(vertex);
|
||||||
|
|
||||||
|
return edges.get(vertex).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||||
|
return degreeIn(vertex) + degreeOut(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int numberOfVertices() {
|
||||||
|
return edges.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int numberOfEdges() {
|
||||||
|
AtomicInteger sum = new AtomicInteger(0);
|
||||||
|
edges.forEach((v, map) -> sum.getAndAdd(map.size()));
|
||||||
|
|
||||||
|
return sum.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||||
|
strategy.visit(this, source, visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graph<V, W> transpose() {
|
||||||
|
Graph<V, W> graph = new MapGraph<>();
|
||||||
|
for (V vertex : edges.keySet())
|
||||||
|
graph.addVertex(vertex);
|
||||||
|
|
||||||
|
edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<V> topologicalSort() throws UnsupportedOperationException {
|
||||||
|
if (!isDAG())
|
||||||
|
throw new UnsupportedOperationException(NOT_DAG);
|
||||||
|
return getTarjan().getTopologicalSort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Set<V>> stronglyConnectedComponents() {
|
||||||
|
return getTarjan().getSCC();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
|
||||||
|
Graph<V, W> sub = new MapGraph<>();
|
||||||
|
Set<V> vertices = new HashSet<>();
|
||||||
|
|
||||||
|
int finalDepth = depth > 0 ? depth : 0;
|
||||||
|
VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
|
||||||
|
int currentDepth = 0;
|
||||||
|
final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>();
|
||||||
|
toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0));
|
||||||
|
vertices.add(source);
|
||||||
|
|
||||||
|
while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) {
|
||||||
|
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst();
|
||||||
|
currentDepth = current.getValue() + 1;
|
||||||
|
final int finalCurrentDepth = currentDepth;
|
||||||
|
|
||||||
|
for (V child : graph.getChildren(current.getKey()))
|
||||||
|
if (!vertices.contains(child)) {
|
||||||
|
toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth));
|
||||||
|
vertices.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
strategy.visit(this, source, null);
|
||||||
|
|
||||||
|
sub.addAllVertices(vertices);
|
||||||
|
for (V vertex : vertices)
|
||||||
|
getChildrenAndWeight(vertex).forEach((child, weight) -> {
|
||||||
|
try {
|
||||||
|
sub.addEdge(vertex, child, weight);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||||
|
checkNullAndExist(source);
|
||||||
|
checkNullAndExist(destination);
|
||||||
|
|
||||||
|
Dijkstra<V, W> dijkstra = getDijkstra(source);
|
||||||
|
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
|
||||||
|
if (path == null)
|
||||||
|
throw new UnsupportedOperationException(NOT_CONNECTED);
|
||||||
|
return new ArrayList<>(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
|
||||||
|
checkNullAndExist(source);
|
||||||
|
return new HashMap<>(getDijkstra(source).getLastDistance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return edges.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple function that set all the memory vars at null if the graph changed
|
||||||
|
*/
|
||||||
|
private void graphChanged() {
|
||||||
|
tarjan = null;
|
||||||
|
dijkstra = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dijkstra<V, W> getDijkstra(V source) {
|
||||||
|
if (dijkstra == null)
|
||||||
|
dijkstra = new HashMap<>();
|
||||||
|
if (dijkstra.get(source) == null) {
|
||||||
|
Dijkstra<V, W> newDijkstra = new Dijkstra<>();
|
||||||
|
newDijkstra.visit(this, source, null);
|
||||||
|
dijkstra.put(source, newDijkstra);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dijkstra.get(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tarjan<V, W> getTarjan() {
|
||||||
|
if (tarjan == null) {
|
||||||
|
tarjan = new Tarjan<>();
|
||||||
|
tarjan.visit(this, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tarjan;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNull(Object object) {
|
||||||
|
if (object == null)
|
||||||
|
throw new NullPointerException(PARAM_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNullAndExist(V vertex) {
|
||||||
|
checkNull(vertex);
|
||||||
|
if (!edges.containsKey(vertex))
|
||||||
|
throw new IllegalArgumentException(VERTEX_NOT_CONTAINED);
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/berack96/sim/util/graph/visit/Dijkstra.java
Normal file
89
src/berack96/sim/util/graph/visit/Dijkstra.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package berack96.sim.util.graph.visit;
|
||||||
|
|
||||||
|
import berack96.sim.util.graph.Graph;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Dijkstra<V, W extends Number> implements VisitStrategy<V, W> {
|
||||||
|
|
||||||
|
private Map<V, List<Graph.Edge<V, W>>> distance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last calculated distance to all the possible destinations<br>
|
||||||
|
* The map contains all the possible vertices that are reachable from the source set in the visit<br>
|
||||||
|
* If there is no path between the destination and the source, then null is returned as accordingly to the map interface<br>
|
||||||
|
* If the visit is not already been done, then the map is null.
|
||||||
|
*
|
||||||
|
* @return the last distance
|
||||||
|
*/
|
||||||
|
public Map<V, List<Graph.Edge<V, W>>> getLastDistance() {
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||||
|
Queue<QueueEntry<V, Integer>> queue = new PriorityQueue<>();
|
||||||
|
Map<V, Integer> dist = new HashMap<>();
|
||||||
|
Map<V, V> prev = new HashMap<>();
|
||||||
|
|
||||||
|
dist.put(source, 0); // Initialization
|
||||||
|
queue.add(new QueueEntry<>(source, 0));
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) { // The main loop
|
||||||
|
QueueEntry<V, Integer> u = queue.poll(); // Remove and return best vertex
|
||||||
|
graph.getChildrenAndWeight(u.entry).forEach((vertex, weight) -> {
|
||||||
|
int alt = dist.get(u.entry) + weight.intValue();
|
||||||
|
Integer distCurrent = dist.get(vertex);
|
||||||
|
if (distCurrent == null || alt < distCurrent) {
|
||||||
|
dist.put(vertex, alt);
|
||||||
|
prev.put(vertex, u.entry);
|
||||||
|
|
||||||
|
QueueEntry<V, Integer> current = new QueueEntry<>(vertex, alt);
|
||||||
|
queue.remove(current);
|
||||||
|
queue.add(current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleaning up the results */
|
||||||
|
distance = new HashMap<>();
|
||||||
|
for (V vertex : prev.keySet()) {
|
||||||
|
List<Graph.Edge<V, W>> path = new LinkedList<>();
|
||||||
|
V child = vertex;
|
||||||
|
V father = prev.get(child);
|
||||||
|
do {
|
||||||
|
Graph.Edge<V, W> edge = new Graph.Edge<>(father, child, graph.getWeight(father, child));
|
||||||
|
path.add(0, edge);
|
||||||
|
child = father;
|
||||||
|
father = prev.get(child);
|
||||||
|
} while (father != null);
|
||||||
|
|
||||||
|
distance.put(vertex, new ArrayList<>(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class QueueEntry<V, W extends Number> implements Comparable<QueueEntry> {
|
||||||
|
final V entry;
|
||||||
|
final W weight;
|
||||||
|
|
||||||
|
QueueEntry(V entry, W weight) {
|
||||||
|
this.entry = entry;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
try {
|
||||||
|
return ((QueueEntry) obj).entry.equals(entry);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(QueueEntry queueEntry) {
|
||||||
|
return this.weight.intValue() - queueEntry.weight.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/berack96/sim/util/graph/visit/Tarjan.java
Normal file
96
src/berack96/sim/util/graph/visit/Tarjan.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package berack96.sim.util.graph.visit;
|
||||||
|
|
||||||
|
import berack96.sim.util.graph.Graph;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Tarjan<V, W extends Number> implements VisitStrategy<V, W> {
|
||||||
|
|
||||||
|
private Set<Set<V>> SCC = null;
|
||||||
|
private List<V> topologicalSort = null;
|
||||||
|
|
||||||
|
private Map<V, Integer> indices = null;
|
||||||
|
private Map<V, Integer> lowLink = null;
|
||||||
|
private Stack<V> stack = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest calculated strongly connected components of the graph.
|
||||||
|
*
|
||||||
|
* @return the latest SCC
|
||||||
|
*/
|
||||||
|
public Set<Set<V>> getSCC() {
|
||||||
|
return SCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest calculated Topological sort of the graph.<br>
|
||||||
|
* If the latest visited graph is not a DAG, it will return null.
|
||||||
|
*
|
||||||
|
* @return the topological order of the DAG
|
||||||
|
*/
|
||||||
|
public List<V> getTopologicalSort() {
|
||||||
|
return topologicalSort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This particular visit strategy use only the graph, so the other parameters are useless.
|
||||||
|
*
|
||||||
|
* @param graph the graph to visit
|
||||||
|
* @param source the source of the visit
|
||||||
|
* @param visit the function to apply at each vertex when they are visited
|
||||||
|
* @throws NullPointerException if the graph is null
|
||||||
|
* @throws IllegalArgumentException doesn't throw this
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||||
|
SCC = new HashSet<>();
|
||||||
|
topologicalSort = new LinkedList<>();
|
||||||
|
|
||||||
|
indices = new HashMap<>();
|
||||||
|
lowLink = new HashMap<>();
|
||||||
|
stack = new Stack<>();
|
||||||
|
Integer index = 0;
|
||||||
|
|
||||||
|
for (V vertex : graph)
|
||||||
|
if (!indices.containsKey(vertex))
|
||||||
|
strongConnect(graph, vertex, index);
|
||||||
|
|
||||||
|
topologicalSort = (graph.numberOfVertices() == SCC.size()) ? new ArrayList<>(topologicalSort) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void strongConnect(Graph<V, W> graph, V vertex, Integer index) {
|
||||||
|
// Set the depth index for v to the smallest unused index
|
||||||
|
indices.put(vertex, index);
|
||||||
|
lowLink.put(vertex, index);
|
||||||
|
index++;
|
||||||
|
stack.push(vertex);
|
||||||
|
|
||||||
|
// Consider successors of v
|
||||||
|
for (V child : graph.getChildren(vertex)) {
|
||||||
|
if (!indices.containsKey(child)) {
|
||||||
|
strongConnect(graph, child, index);
|
||||||
|
lowLink.put(vertex, Math.min(lowLink.get(vertex), lowLink.get(child)));
|
||||||
|
} else if (stack.contains(child)) {
|
||||||
|
// Successor w is in stack S and hence in the current SCC
|
||||||
|
// If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored
|
||||||
|
// Note: The next line may look odd - but is correct.
|
||||||
|
// It says w.index not w.lowlink; that is deliberate and from the original paper
|
||||||
|
lowLink.put(vertex, Math.min(lowLink.get(vertex), indices.get(child)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If v is a root node, pop the stack and generate an SCC
|
||||||
|
if (lowLink.get(vertex).equals(indices.get(vertex))) {
|
||||||
|
Set<V> newComponent = new HashSet<>();
|
||||||
|
V temp;
|
||||||
|
do {
|
||||||
|
temp = stack.pop();
|
||||||
|
topologicalSort.add(0, temp);
|
||||||
|
newComponent.add(temp);
|
||||||
|
} while (!temp.equals(vertex));
|
||||||
|
|
||||||
|
SCC.add(newComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package berack96.test.sim;
|
package berack96.test.sim;
|
||||||
|
|
||||||
import berack96.sim.util.graph.Graph;
|
import berack96.sim.util.graph.Graph;
|
||||||
|
import berack96.sim.util.graph.MapGraph;
|
||||||
import berack96.sim.util.graph.visit.BFS;
|
import berack96.sim.util.graph.visit.BFS;
|
||||||
import berack96.sim.util.graph.visit.DFS;
|
import berack96.sim.util.graph.visit.DFS;
|
||||||
import berack96.sim.util.graph.visit.VisitStrategy;
|
import berack96.sim.util.graph.visit.VisitStrategy;
|
||||||
@@ -21,7 +22,7 @@ public class TestGraph {
|
|||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
// Change here the instance for changing all the test for that particular class
|
// Change here the instance for changing all the test for that particular class
|
||||||
graph = null;
|
graph = new MapGraph<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -312,6 +313,14 @@ public class TestGraph {
|
|||||||
new Graph.Edge<>("4", "6", 6),
|
new Graph.Edge<>("4", "6", 6),
|
||||||
new Graph.Edge<>("5", "3", 9),
|
new Graph.Edge<>("5", "3", 9),
|
||||||
new Graph.Edge<>("5", "4", 5));
|
new Graph.Edge<>("5", "4", 5));
|
||||||
|
|
||||||
|
shouldThrow(nullException, () -> graph.edgesOf(null));
|
||||||
|
shouldThrow(notException, () -> graph.edgesOf("rew"));
|
||||||
|
shouldContain(graph.edgesOf("5"),
|
||||||
|
new Graph.Edge<>("2", "5", 4),
|
||||||
|
new Graph.Edge<>("3", "5", 2),
|
||||||
|
new Graph.Edge<>("5", "3", 9),
|
||||||
|
new Graph.Edge<>("5", "4", 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user