Path refactoring net.berack is now the corect pakage, update java to 23

This commit is contained in:
2025-03-12 09:25:57 +01:00
parent 07305b82f8
commit aba1f55bc0
48 changed files with 227 additions and 228 deletions

View 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;
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}