Path refactoring net.berack is now the corect pakage, update java to 23
This commit is contained in:
52
src/main/java/net/berack/upo/graph/visit/BFS.java
Normal file
52
src/main/java/net/berack/upo/graph/visit/BFS.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* Breadth-first search<br>
|
||||
* The algorithm starts at the root node and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
|
||||
*
|
||||
* @param <V> the vertex of the graph
|
||||
* @author Berack96
|
||||
*/
|
||||
public class BFS<V> implements VisitStrategy<V> {
|
||||
|
||||
private int maxDepth = -1;
|
||||
|
||||
public BFS<V> setMaxDepth(int depth) {
|
||||
this.maxDepth = depth;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
final LinkedList<V> toVisitChildren = new LinkedList<>();
|
||||
|
||||
toVisitChildren.push(source);
|
||||
if (visit != null)
|
||||
visit.accept(source);
|
||||
info.setVisited(source);
|
||||
|
||||
while (!toVisitChildren.isEmpty()) {
|
||||
V current = toVisitChildren.removeFirst();
|
||||
if (maxDepth > -1 && info.getDepth(current) >= maxDepth)
|
||||
break;
|
||||
|
||||
for (V child : graph.getChildren(current))
|
||||
if (!info.isDiscovered(child)) {
|
||||
toVisitChildren.addLast(child);
|
||||
|
||||
info.setVisited(child);
|
||||
info.setParent(current, child);
|
||||
if (visit != null)
|
||||
visit.accept(child);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
49
src/main/java/net/berack/upo/graph/visit/DFS.java
Normal file
49
src/main/java/net/berack/upo/graph/visit/DFS.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* Depth-first search<br>
|
||||
* The algorithm starts at the root node and explores as far as possible along each branch before backtracking.
|
||||
*
|
||||
* @param <V> the vertex of the graph
|
||||
* @author Berack96
|
||||
*/
|
||||
public class DFS<V> implements VisitStrategy<V> {
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
final Stack<V> toVisit = new Stack<>();
|
||||
|
||||
toVisit.push(source);
|
||||
|
||||
while (!toVisit.isEmpty()) {
|
||||
V current = toVisit.peek();
|
||||
boolean hasChildToVisit = false;
|
||||
Iterator<V> iter = graph.getChildren(current).iterator();
|
||||
|
||||
while (iter.hasNext() && !hasChildToVisit) {
|
||||
V child = iter.next();
|
||||
if (!info.isDiscovered(child)) {
|
||||
hasChildToVisit = true;
|
||||
toVisit.push(child);
|
||||
info.setParent(current, child);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChildToVisit) {
|
||||
toVisit.pop();
|
||||
info.setVisited(current);
|
||||
if (visit != null)
|
||||
visit.accept(current);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
109
src/main/java/net/berack/upo/graph/visit/Dijkstra.java
Normal file
109
src/main/java/net/berack/upo/graph/visit/Dijkstra.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitDistance;
|
||||
|
||||
/**
|
||||
* Class that implements the Dijkstra algorithm and uses it for getting all the distance from a source
|
||||
*
|
||||
* @param <V> vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Dijkstra<V> implements VisitDistance<V> {
|
||||
|
||||
private Map<V, List<Edge<V>>> distance = null;
|
||||
private V source = null;
|
||||
|
||||
@Override
|
||||
public Map<V, List<Edge<V>>> getLastDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getLastSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
Queue<QueueEntry> queue = new PriorityQueue<>();
|
||||
Map<V, Integer> dist = Graph.getDefaultMap();
|
||||
Map<V, V> prev = Graph.getDefaultMap();
|
||||
|
||||
this.source = source;
|
||||
dist.put(source, 0); // Initialization
|
||||
queue.add(new QueueEntry(source, 0));
|
||||
|
||||
while (!queue.isEmpty()) { // The main loop
|
||||
QueueEntry u = queue.poll(); // Remove and return best vertex
|
||||
|
||||
info.setVisited(u.entry);
|
||||
if (visit != null)
|
||||
visit.accept(u.entry);
|
||||
|
||||
for (V child : graph.getChildren(u.entry)) {
|
||||
info.setDiscovered(child);
|
||||
int alt = dist.get(u.entry) + graph.getWeight(u.entry, child);
|
||||
Integer distCurrent = dist.get(child);
|
||||
|
||||
if (distCurrent == null || alt < distCurrent) {
|
||||
dist.put(child, alt);
|
||||
prev.put(child, u.entry);
|
||||
|
||||
QueueEntry current = new QueueEntry(child, alt);
|
||||
queue.remove(current);
|
||||
queue.add(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleaning up the results */
|
||||
distance = Graph.getDefaultMap();
|
||||
for (V vertex : prev.keySet()) {
|
||||
List<Edge<V>> path = new LinkedList<>();
|
||||
V child = vertex;
|
||||
V father = prev.get(child);
|
||||
do {
|
||||
Edge<V> edge = new Edge<>(father, child, graph.getWeight(father, child));
|
||||
path.add(0, edge);
|
||||
info.setParent(father, child);
|
||||
child = father;
|
||||
father = prev.get(child);
|
||||
} while (father != null);
|
||||
|
||||
distance.put(vertex, new ArrayList<>(path));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
private class QueueEntry implements Comparable<QueueEntry> {
|
||||
final V entry;
|
||||
final int weight;
|
||||
|
||||
QueueEntry(V entry, int weight) {
|
||||
this.entry = entry;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return ((QueueEntry) obj).entry.equals(entry);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(QueueEntry queueEntry) {
|
||||
double ret = this.weight - queueEntry.weight;
|
||||
return ret==0? 0: ret<0? -1:1;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/java/net/berack/upo/graph/visit/Kruskal.java
Normal file
44
src/main/java/net/berack/upo/graph/visit/Kruskal.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphUndirected;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitMST;
|
||||
import net.berack.upo.graph.visit.struct.QuickFind;
|
||||
import net.berack.upo.graph.visit.struct.UnionFind;
|
||||
|
||||
/**
|
||||
* Class that implement the algorithm discovered by Kruskal for the minimum spanning forest
|
||||
* for a given {@link GraphUndirected}
|
||||
*
|
||||
* @param <V> The vertex of the graph
|
||||
*/
|
||||
public class Kruskal<V> implements VisitMST<V> {
|
||||
private Set<Edge<V>> mst;
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> getMST() {
|
||||
return mst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
UnionFind<V> sets = new QuickFind<>();
|
||||
sets.makeSetAll(graph.vertices());
|
||||
|
||||
List<Edge<V>> edges = new ArrayList<>(graph.edges());
|
||||
Collections.sort(edges);
|
||||
|
||||
mst = Graph.getDefaultSet();
|
||||
Iterator<Edge<V>> iter = edges.iterator();
|
||||
while (iter.hasNext() && sets.size() > 1) {
|
||||
Edge<V> edge = iter.next();
|
||||
if (sets.union(edge.getSource(), edge.getDestination()))
|
||||
mst.add(edge);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
62
src/main/java/net/berack/upo/graph/visit/Prim.java
Normal file
62
src/main/java/net/berack/upo/graph/visit/Prim.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphUndirected;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitMST;
|
||||
|
||||
/**
|
||||
* Class that implement the algorithm discovered by Prim for the minimum spanning forest
|
||||
* for a given {@link GraphUndirected}
|
||||
*
|
||||
* @param <V> The vertex of the graph
|
||||
*/
|
||||
public class Prim<V> implements VisitMST<V> {
|
||||
|
||||
private Set<Edge<V>> mst;
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> getMST() {
|
||||
return mst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
mst = Graph.getDefaultSet();
|
||||
Set<V> vertices = graph.vertices();
|
||||
|
||||
if (source == null)
|
||||
source = vertices.iterator().next();
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
V current = source;
|
||||
|
||||
do {
|
||||
if (current == null)
|
||||
current = vertices.iterator().next();
|
||||
|
||||
Edge<V> min = null;
|
||||
for (Edge<V> edge : graph.edgesOf(current))
|
||||
if (vertices.contains(edge.getDestination()))
|
||||
min = (min == null || edge.getWeight() < min.getWeight() ? edge : min);
|
||||
|
||||
info.setParent(source, current);
|
||||
info.setVisited(current);
|
||||
if (visit != null)
|
||||
visit.accept(current);
|
||||
|
||||
if (min == null)
|
||||
current = null;
|
||||
else {
|
||||
vertices.remove(current);
|
||||
source = min.getSource();
|
||||
current = min.getDestination();
|
||||
mst.add(min);
|
||||
}
|
||||
} while (vertices.size() != 0);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
108
src/main/java/net/berack/upo/graph/visit/Tarjan.java
Normal file
108
src/main/java/net/berack/upo/graph/visit/Tarjan.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitSCC;
|
||||
import net.berack.upo.graph.VisitTopological;
|
||||
|
||||
/**
|
||||
* Class that implements the Tarjan algorithm and uses it for getting the SCC and the topological sort
|
||||
*
|
||||
* @param <V> vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Tarjan<V> implements VisitSCC<V>, VisitTopological<V> {
|
||||
|
||||
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;
|
||||
private VisitInfo<V> info = null;
|
||||
|
||||
@Override
|
||||
public Set<Set<V>> getSCC() {
|
||||
return SCC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> getTopologicalSort() {
|
||||
return topologicalSort;
|
||||
}
|
||||
|
||||
/**
|
||||
* This particular visit strategy use only the graph and the visit, so the source param is not needed.
|
||||
*
|
||||
* @param graph the graph to visit
|
||||
* @param source not needed
|
||||
* @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 VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
SCC = Graph.getDefaultSet();
|
||||
topologicalSort = new ArrayList<>(graph.size());
|
||||
info = null;
|
||||
|
||||
indices = Graph.getDefaultMap();
|
||||
lowLink = Graph.getDefaultMap();
|
||||
stack = new Stack<>();
|
||||
int index = 0;
|
||||
|
||||
for (V vertex : graph) {
|
||||
if (info == null)
|
||||
info = new VisitInfo<>(vertex);
|
||||
if (!indices.containsKey(vertex))
|
||||
strongConnect(graph, vertex, index, visit);
|
||||
}
|
||||
|
||||
topologicalSort = (graph.size() == SCC.size()) ? topologicalSort : null;
|
||||
return info;
|
||||
}
|
||||
|
||||
private void strongConnect(Graph<V> graph, V vertex, Integer index, Consumer<V> visit) {
|
||||
// Set the depth index for v to the smallest unused index
|
||||
indices.put(vertex, index);
|
||||
lowLink.put(vertex, index);
|
||||
index++;
|
||||
stack.push(vertex);
|
||||
info.setDiscovered(vertex);
|
||||
|
||||
// Consider successors of v
|
||||
for (V child : graph.getChildren(vertex)) {
|
||||
if (!indices.containsKey(child)) {
|
||||
info.setParent(vertex, child);
|
||||
strongConnect(graph, child, index, visit);
|
||||
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 = Graph.getDefaultSet();
|
||||
V temp;
|
||||
do {
|
||||
temp = stack.pop();
|
||||
topologicalSort.add(0, temp);
|
||||
newComponent.add(temp);
|
||||
|
||||
info.setVisited(temp);
|
||||
if (visit != null)
|
||||
visit.accept(temp);
|
||||
|
||||
} while (!temp.equals(vertex));
|
||||
|
||||
SCC.add(newComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
327
src/main/java/net/berack/upo/graph/visit/VisitInfo.java
Normal file
327
src/main/java/net/berack/upo/graph/visit/VisitInfo.java
Normal file
@@ -0,0 +1,327 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* The class used for getting the info of the visit.<br>
|
||||
* It could be used with the algorithm of the visit for set some useful data.
|
||||
*
|
||||
* @param <V> the vertex of the visit
|
||||
* @author Berack96
|
||||
*/
|
||||
public class VisitInfo<V> implements Iterable<VisitInfo<V>.VertexInfo> {
|
||||
private static final int NOT_SET = -1;
|
||||
|
||||
private final Map<V, VertexInfo> vertices;
|
||||
private final V source;
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* Need a source for initialize the basic values
|
||||
*
|
||||
* @param source the source of the visit
|
||||
* @throws NullPointerException if the source is null
|
||||
*/
|
||||
public VisitInfo(V source) {
|
||||
if (source == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
this.vertices = new Hashtable<>();
|
||||
this.time = 0;
|
||||
this.source = source;
|
||||
setDiscovered(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source of the visit.
|
||||
*
|
||||
* @return the source vertex where it's started the visit
|
||||
*/
|
||||
public V getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent of a particular vertex.<br>
|
||||
* The parent of a vertex is the one that has discovered it<br>
|
||||
* If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned.
|
||||
*
|
||||
* @param vertex the child vertex
|
||||
* @return the parent of the child
|
||||
* @throws IllegalArgumentException if the vertex has not been discovered yet or is null
|
||||
*/
|
||||
public V getParentOf(V vertex) throws IllegalArgumentException {
|
||||
VertexInfo info = vertices.get(vertex);
|
||||
if (!isDiscovered(vertex))
|
||||
throw new IllegalArgumentException();
|
||||
return info.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time of the vertex when it is discovered in the visit.<br>
|
||||
* For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}<br>
|
||||
* The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time<br>
|
||||
*
|
||||
* @param vertex the vertex needed
|
||||
* @return the time of it's discovery
|
||||
* @throws IllegalArgumentException if the vertex is not discovered
|
||||
* @throws NullPointerException if the vertex is null
|
||||
*/
|
||||
public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException {
|
||||
VertexInfo info = vertices.get(vertex);
|
||||
long time = (info == null) ? NOT_SET : info.timeDiscovered;
|
||||
|
||||
if(time == NOT_SET)
|
||||
throw new IllegalArgumentException();
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time when the vertex is visited by the algorithm<br>
|
||||
* For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}<br>
|
||||
* The time starts at 0 and for each vertex discovered or visited is increased by one<br>
|
||||
*
|
||||
* @param vertex the vertex needed
|
||||
* @return the time of it's visit
|
||||
* @throws IllegalArgumentException if the vertex is not visited
|
||||
* @throws NullPointerException if the vertex is null
|
||||
*/
|
||||
public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException {
|
||||
VertexInfo info = vertices.get(vertex);
|
||||
long time = (info == null) ? NOT_SET : info.timeVisited;
|
||||
|
||||
if(time == NOT_SET)
|
||||
throw new IllegalArgumentException();
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* The depth of the vertex when it was first discovered.
|
||||
*
|
||||
* @param vertex the vertex needed
|
||||
* @return the depth of it's discovery
|
||||
* @throws IllegalArgumentException if the vertex is not discovered
|
||||
* @throws NullPointerException if the vertex is null
|
||||
*/
|
||||
public long getDepth(V vertex) throws IllegalArgumentException, NullPointerException {
|
||||
VertexInfo info = vertices.get(vertex);
|
||||
long depth = (info == null) ? NOT_SET : info.depth;
|
||||
|
||||
if(depth == NOT_SET)
|
||||
throw new IllegalArgumentException();
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a vertex is discovered or not
|
||||
*
|
||||
* @param vertex the vertex chosen
|
||||
* @return true if is discovered
|
||||
*/
|
||||
public boolean isDiscovered(V vertex) throws NullPointerException {
|
||||
try {
|
||||
return vertices.get(vertex).timeDiscovered != NOT_SET;
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the vertex is visited or not
|
||||
*
|
||||
* @param vertex the vertex chosen
|
||||
* @return true if is visited
|
||||
*/
|
||||
public boolean isVisited(V vertex) throws NullPointerException {
|
||||
try {
|
||||
return vertices.get(vertex).timeVisited != NOT_SET;
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.<br>
|
||||
* Next this call it will be possible to get the time of visit of that vertex<br>
|
||||
* Does nothing if the vertex has already been visited.
|
||||
*
|
||||
* @param vertex the vertex that has been visited
|
||||
*/
|
||||
synchronized void setVisited(V vertex) {
|
||||
setDiscovered(vertex);
|
||||
VertexInfo info = vertices.get(vertex);
|
||||
if(info.timeVisited != NOT_SET)
|
||||
return;
|
||||
|
||||
info.timeVisited = time;
|
||||
time++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery<br>
|
||||
* Does nothing if the vertex has already been discovered.
|
||||
*
|
||||
* @param vertex the vertex that has been discovered
|
||||
*/
|
||||
synchronized void setDiscovered(V vertex) {
|
||||
VertexInfo info = vertices.computeIfAbsent(vertex, _ -> new VertexInfo(vertex));
|
||||
if(info.timeDiscovered != NOT_SET)
|
||||
return;
|
||||
|
||||
info.timeDiscovered = time;
|
||||
info.depth = 0;
|
||||
time++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent of a particular vertex<br>
|
||||
* The parent of a vertex is the one that has discovered it<br>
|
||||
* If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called<br>
|
||||
*
|
||||
* @param parent the vertex that is the parent
|
||||
* @param child the vertex discovered
|
||||
* @throws IllegalArgumentException if the parent is not already discovered
|
||||
*/
|
||||
synchronized void setParent(V parent, V child) throws IllegalArgumentException {
|
||||
if (!isDiscovered(parent))
|
||||
throw new IllegalArgumentException(parent.toString());
|
||||
|
||||
setDiscovered(child);
|
||||
VertexInfo info = vertices.get(child);
|
||||
info.parent = parent;
|
||||
info.depth = vertices.get(parent).depth + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the visited vertices so far.
|
||||
*
|
||||
* @return the visited vertices
|
||||
*/
|
||||
public Set<V> getVisited() {
|
||||
Set<V> visited = new HashSet<>(vertices.size());
|
||||
vertices.forEach((vert, info) -> {
|
||||
if(info.timeVisited != NOT_SET)
|
||||
visited.add(vert);
|
||||
});
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the discovered vertices so far.
|
||||
*
|
||||
* @return the discovered vertices
|
||||
*/
|
||||
public Set<V> getDiscovered() {
|
||||
Set<V> discovered = new HashSet<>(vertices.size());
|
||||
vertices.forEach((vert, info) -> {
|
||||
if(info.timeDiscovered != NOT_SET)
|
||||
discovered.add(vert);
|
||||
});
|
||||
return discovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through all the vertices that are discovered.<br>
|
||||
* The vertices will be ordered by the time of their discover.
|
||||
*
|
||||
* @param consumer the function to apply to each
|
||||
*/
|
||||
public void forEachDiscovered(Consumer<? super VertexInfo> consumer) {
|
||||
Queue<VertexInfo> queue = new PriorityQueue<>();
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeDiscovered != NOT_SET)
|
||||
queue.offer(new VertexInfo(info, false));
|
||||
});
|
||||
|
||||
while (!queue.isEmpty())
|
||||
consumer.accept(queue.poll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through all the vertices that are visited.<br>
|
||||
* The vertices will be ordered by the time of their visit.
|
||||
*
|
||||
* @param consumer the function to apply to each
|
||||
*/
|
||||
public void forEachVisited(Consumer<? super VertexInfo> consumer) {
|
||||
Queue<VertexInfo> queue = new PriorityQueue<>();
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeVisited != NOT_SET)
|
||||
queue.offer(new VertexInfo(info, true));
|
||||
});
|
||||
|
||||
while (!queue.isEmpty())
|
||||
consumer.accept(queue.poll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<VertexInfo> iterator() {
|
||||
List<VertexInfo> list = new ArrayList<>(vertices.size() * 2);
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeDiscovered != NOT_SET)
|
||||
list.add(new VertexInfo(info, false));
|
||||
if (info.timeVisited != NOT_SET)
|
||||
list.add(new VertexInfo(info, true));
|
||||
});
|
||||
Collections.sort(list);
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used mainly for storing the data of the visit
|
||||
*/
|
||||
public class VertexInfo implements Comparable<VertexInfo> {
|
||||
public V vertex;
|
||||
public V parent;
|
||||
public long timeDiscovered;
|
||||
public long timeVisited;
|
||||
public long depth;
|
||||
private final boolean compareVisited;
|
||||
|
||||
private VertexInfo(V vertex) {
|
||||
this.vertex = vertex;
|
||||
this.timeDiscovered = NOT_SET;
|
||||
this.timeVisited = NOT_SET;
|
||||
this.depth = NOT_SET;
|
||||
this.compareVisited = false;
|
||||
}
|
||||
|
||||
private VertexInfo(VertexInfo info, boolean compare) {
|
||||
this.vertex = info.vertex;
|
||||
this.parent = info.parent;
|
||||
this.timeDiscovered = info.timeDiscovered;
|
||||
this.timeVisited = info.timeVisited;
|
||||
this.depth = info.depth;
|
||||
this.compareVisited = compare;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return obj instanceof VisitInfo.VertexInfo && obj.toString().equals(toString());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(VertexInfo other) {
|
||||
long compareThis = compareVisited ? timeVisited : timeDiscovered;
|
||||
long compareOther = other.compareVisited ? other.timeVisited : other.timeDiscovered;
|
||||
return (int) (compareThis - compareOther);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s (%3d) [D:%3d, V:%3d]", parent, vertex, depth, timeDiscovered, timeVisited);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.berack.upo.graph.visit.struct;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link UnionFind} interface with priority to the find function.
|
||||
*
|
||||
* @param <X> the elements to search and merge
|
||||
*/
|
||||
public class QuickFind<X> implements UnionFind<X> {
|
||||
Map<X, Collection<X>> struct = Graph.getDefaultMap();
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return struct.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeSetAll(Collection<X> elements) throws NullPointerException {
|
||||
Map<X, Collection<X>> temp = Graph.getDefaultMap();
|
||||
for (X elem : elements)
|
||||
temp.computeIfAbsent(elem, new AddElement());
|
||||
struct.putAll(temp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeSet(X element) throws NullPointerException {
|
||||
if (element == null)
|
||||
throw new NullPointerException();
|
||||
struct.computeIfAbsent(element, new AddElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException {
|
||||
element1 = find(element1);
|
||||
element2 = find(element2);
|
||||
if (element1 == null || element2 == null)
|
||||
throw new IllegalArgumentException();
|
||||
if (element1 == element2)
|
||||
return false;
|
||||
|
||||
return struct.get(element1).addAll(struct.remove(element2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public X find(X element) throws NullPointerException {
|
||||
if (element == null)
|
||||
throw new NullPointerException();
|
||||
if (struct.containsKey(element))
|
||||
return element;
|
||||
|
||||
AtomicReference<X> toReturn = new AtomicReference<>(null);
|
||||
struct.forEach((key, collection) -> {
|
||||
if (collection.contains(element))
|
||||
toReturn.set(key);
|
||||
});
|
||||
return toReturn.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stupid class for implementing the adding of a new element
|
||||
*/
|
||||
private class AddElement implements Function<X, Set<X>> {
|
||||
@Override
|
||||
public Set<X> apply(X x) {
|
||||
Set<X> coll = Graph.getDefaultSet();
|
||||
coll.add(x);
|
||||
return coll;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.berack.upo.graph.visit.struct;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Basic interface for the UnionFind tree sets
|
||||
*
|
||||
* @param <X> the object
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface UnionFind<X> {
|
||||
|
||||
/**
|
||||
* Indicate how many different sets there are.
|
||||
*
|
||||
* @return the number of sets
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* It creates the single element set for every element in the collection
|
||||
*
|
||||
* @param elements the collection of the elements
|
||||
* @throws NullPointerException in the case of the set being null
|
||||
*/
|
||||
void makeSetAll(Collection<X> elements) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Creates the single element set for the element
|
||||
*
|
||||
* @param element the element to insert
|
||||
* @throws NullPointerException in the case of a null element
|
||||
*/
|
||||
void makeSet(X element) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Merge the tho elements into a single set.<br>
|
||||
* In the case that the two elements are in the same set it returns false.
|
||||
*
|
||||
* @param element1 an element of a set
|
||||
* @param element2 an element of another set
|
||||
* @return true in the case of a successful merge, false otherwise
|
||||
* @throws NullPointerException in the case of a null element
|
||||
* @throws IllegalArgumentException in the case of an element not in the sets
|
||||
*/
|
||||
boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Returns the element representing the set in which the element passed resides.<br>
|
||||
* In case of an element not found then it's returned null
|
||||
*
|
||||
* @param element the element in the set
|
||||
* @return the representing element of the set found
|
||||
* @throws NullPointerException in the case of a null element
|
||||
*/
|
||||
X find(X element) throws NullPointerException;
|
||||
}
|
||||
Reference in New Issue
Block a user