From d6f43a646f062b962bc106534a2f8246e3aea7f3 Mon Sep 17 00:00:00 2001 From: Giovanni Di Grezia Date: Wed, 28 May 2014 00:52:03 +0200 Subject: [PATCH] continuata implementazione di graph. bfs dfs kruskal dijkstra --- graph/BFS.java | 131 +++++++++++++++++++++++++++++++++++++++ graph/ComponentsBFS.java | 39 ++++++++++++ graph/ComponentsDFS.java | 35 +++++++++++ graph/DFS.java | 106 +++++++++++++++++++++++++++++++ graph/Dijkstra.java | 102 ++++++++++++++++++++++++++++++ graph/FindCycleDFS.java | 58 +++++++++++++++++ graph/FindPathDFS.java | 43 +++++++++++++ graph/Kruskal.java | 52 ++++++++++++++++ graph/gioel.rar | Bin 0 -> 8234 bytes 9 files changed, 566 insertions(+) create mode 100644 graph/BFS.java create mode 100644 graph/ComponentsBFS.java create mode 100644 graph/ComponentsDFS.java create mode 100644 graph/DFS.java create mode 100644 graph/Dijkstra.java create mode 100644 graph/FindCycleDFS.java create mode 100644 graph/FindPathDFS.java create mode 100644 graph/Kruskal.java create mode 100644 graph/gioel.rar diff --git a/graph/BFS.java b/graph/BFS.java new file mode 100644 index 0000000..fe013b1 --- /dev/null +++ b/graph/BFS.java @@ -0,0 +1,131 @@ + +//gioel + +package graph; + + +import arraylist.ArrayIndexList; +import arraylist.IndexList; +import position.NodePositionList; +import position.PositionList; + +import java.security.InvalidKeyException; + +public abstract class BFS { + protected Graph graph; + protected Vertex start; + protected I info; + protected R visitResult; + protected static Object STATUS = new Object(); + protected static Object VISITED = new Object(); + protected static Object UNVISITED = new Object(); + + protected IndexList>> layers; + + public R execute(Graph g, Vertex s, I in) throws InvalidKeyException{ + graph = g; + layers = new ArrayIndexList>>( + graph.numVertices()); + start = s; + info = in; + for (Vertex v : graph.vertices()) + unVisit(v); + for (Edge e : graph.edges()) + unVisit(e); + + setup(); + return finalResult(bfsTraversal(start)); + } + + protected void setup() { + } + + protected R bfsTraversal(Vertex v) throws InvalidKeyException{ + initResult(); + if (!isDone()) + startVisit(v); + + if (!isDone()) { + visit(v); + + layers.add(0, new NodePositionList>()); + layers.get(0).addLast(v); + } + + int i = 0; + while (!layers.get(i).isEmpty()) { + layers.add(i + 1, new NodePositionList>()); + + for (Vertex vertexInLayer : layers.get(i)) { + for (Edge e : graph.incidentEdges(vertexInLayer)) { + if (!isVisited(e)) { + visit(e); + Vertex w = graph.opposite(vertexInLayer, e); + if (!isVisited(w)) { + traverseDiscovery(e, vertexInLayer); + + if (isDone()) + break; + + visit(w); + layers.get(i + 1).addLast(w); + + if (isDone()) + break; + } else { + traverseCross(e, v); + if (isDone()) + break; + } + } + } + + if (!isDone()) + finishVisit(vertexInLayer); + } + + i++; + } + + return result(); + } + + protected void initResult() { + } + + protected void startVisit(Vertex v) { + } + + protected void traverseCross(Edge e, Vertex v) { + } + + protected void traverseDiscovery(Edge e, Vertex v) { + } + + protected void finishVisit(Vertex v) { + } + + protected boolean isDone() { + return false; + } + + protected R result() { + return null; + } + + protected R finalResult(R r) { + return null; + } + + protected void visit(DecorablePosition p) throws InvalidKeyException{ + p.put(STATUS, VISITED); + } + + protected void unVisit(DecorablePosition p) throws InvalidKeyException { + p.put(STATUS, UNVISITED); + } + + protected boolean isVisited(DecorablePosition p) throws InvalidKeyException { + return p.get(STATUS) == VISITED; + } +} diff --git a/graph/ComponentsBFS.java b/graph/ComponentsBFS.java new file mode 100644 index 0000000..864d35a --- /dev/null +++ b/graph/ComponentsBFS.java @@ -0,0 +1,39 @@ +//gioel + +package graph; + +import java.security.InvalidKeyException; + +public class ComponentsBFS extends BFS { + protected Integer compNumber; + protected Object COMPONENT = new Object(); + + protected void setup() { + compNumber = 1; + } + + protected void startVisit(Vertex v) { + try { + v.put(COMPONENT, compNumber); + } + catch (InvalidKeyException e){ + + } + } + + protected Integer finalResult(Integer bfsResult) { + try { + for(Vertex v : graph.vertices()) + if(!isVisited(v)) { + compNumber++; + bfsTraversal(v); + } + + return compNumber; + } + catch (InvalidKeyException e){ + + } + return null; + } +} diff --git a/graph/ComponentsDFS.java b/graph/ComponentsDFS.java new file mode 100644 index 0000000..ac40c97 --- /dev/null +++ b/graph/ComponentsDFS.java @@ -0,0 +1,35 @@ +package graph; + +//begin#fragment CC + +import java.security.InvalidKeyException; + +/** This class extends DFS to compute the connected components of a graph. */ +public class ComponentsDFS extends DFS { + protected Integer compNumber; // Connected component number + protected Object COMPONENT = new Object(); // Connected comp. selector + protected void setup() { compNumber = 1; } + protected void startVisit(Vertex v) { + try{ + v.put(COMPONENT, compNumber); + } + catch (InvalidKeyException e){ + // + } + } + protected Integer finalResult(Integer dfsResult) { + try{ + for (Vertex v : graph.vertices()) // check for any unvisited vertices + if (v.get(STATUS) == UNVISITED) { + compNumber += 1; // we have found another connected component + dfsTraversal(v); // visit all the vertices of this component + } + return compNumber; + } + catch (InvalidKeyException e){ + // + } + return null; + } +} +//end#fragment CC diff --git a/graph/DFS.java b/graph/DFS.java new file mode 100644 index 0000000..bc6e7a4 --- /dev/null +++ b/graph/DFS.java @@ -0,0 +1,106 @@ +package graph; + + +import java.security.InvalidKeyException; + +/** Generic DFS traversal of a graph using the template method pattern. + * Parameterized types: + * V, the type for the elements stored at vertices + * E, the type for the elements stored at edges + * I, the type for the information object passed to the execute method + * R, the type for the result object returned by the DFS + */ +public class DFS { + protected Graph graph; // The graph being traversed + protected Vertex start; // The start vertex for the DFS + protected I info; // Information object passed to DFS + protected R visitResult; // The result of a recursive traversal call + protected static Object STATUS = new Object(); // The status attribute + protected static Object VISITED = new Object(); // Visited value + protected static Object UNVISITED = new Object(); // Unvisited value + + + + /** Execute a depth first search traversal on graph g, starting + * from a start vertex s, passing in an information object (in) */ + public R execute(Graph g, Vertex s, I in) throws InvalidKeyException { + graph = g; + start = s; + info = in; + for(Vertex v: graph.vertices()) unVisit(v); // mark vertices as unvisited + for(Edge e: graph.edges()) unVisit(e); // mark edges as unvisited + setup(); // perform any necessary setup prior to DFS traversal + return finalResult(dfsTraversal(start)); + } + /** Recursive template method for a generic DFS traversal. */ + protected R dfsTraversal(Vertex v) throws InvalidKeyException{ + initResult(); + if (!isDone()) + startVisit(v); + if (!isDone()) { + visit(v); + for (Edge e: graph.incidentEdges(v)) { + if (!isVisited(e)) { + // found an unexplored edge, explore it + visit(e); + Vertex w = graph.opposite(v, e); + if (!isVisited(w)) { + // w is unexplored, this is a discovery edge + traverseDiscovery(e, v); + if (isDone()) break; + visitResult = dfsTraversal(w); // get result from DFS-tree child + if (isDone()) break; + } + else { + // w is explored, this is a back edge + traverseBack(e, v); + if (isDone()) break; + } + } + } + } + if(!isDone()) + finishVisit(v); + return result(); + } + //end#fragment DFS2 + + //begin#fragment decorations + /** Mark a position (vertex or edge) as visited. */ + protected void visit(DecorablePosition p) throws InvalidKeyException{ + p.put(STATUS, VISITED); + } + /** Mark a position (vertex or edge) as unvisited. */ + protected void unVisit(DecorablePosition p) throws InvalidKeyException { + p.put(STATUS, UNVISITED); + } + /** Test if a position (vertex or edge) has been visited. */ + protected boolean isVisited(DecorablePosition p) throws InvalidKeyException { + return (p.get(STATUS) == VISITED); + } +//end#fragment decorations + + // Auxiliary methods (all initially null) for specializing a generic DFS +//begin#fragment auxiliary + /** Setup method that is called prior to the DFS execution. */ + protected void setup() {} + /** Initializes result (called first, once per vertex visited). */ + protected void initResult() {} + /** Called when we encounter a vertex (v). */ + protected void startVisit(Vertex v) {} + /** Called after we finish the visit for a vertex (v). */ + protected void finishVisit(Vertex v) {} + /** Called when we traverse a discovery edge (e) from a vertex (from). */ + protected void traverseDiscovery(Edge e, Vertex from) {} + /** Called when we traverse a back edge (e) from a vertex (from). */ + protected void traverseBack(Edge e, Vertex from) {} + /** Determines whether the traversal is done early. */ + protected boolean isDone() { return false; /* default value */ } + /** Returns a result of a visit (if needed). */ + protected R result() { return null; /* default value */ } + /** Returns the final result of the DFS execute method. */ + protected R finalResult(R r) { return r; /* default value */ } +//end#fragment auxiliary +//begin#fragment Tail +} // end of DFS class +//end#fragment Tail diff --git a/graph/Dijkstra.java b/graph/Dijkstra.java new file mode 100644 index 0000000..15688a1 --- /dev/null +++ b/graph/Dijkstra.java @@ -0,0 +1,102 @@ +package graph; + +import priorityqueue.AdaptablePriorityQueue; +import priorityqueue.Entry; +import priorityqueue.heap.HeapAdaptablePriorityQueue; +import utility.DefaultComparator; + +import java.security.InvalidKeyException; + +/** + * Dijkstra's algorithm for the single-source shortest path problem in + * an undirected graph whose edges have integer weights. + * + *

To execute the algorithm, use the {@link + * #execute(Graph,Vertex,Object) execute} method, and then make + * subsequent calls to the {@link #getDist(Vertex) getDist} method to + * obtain the shortest distance from the start to any given vertex. + * + * @author Roberto Tamassia, Michael Goodrich, Eric Zamore + */ + +//begin#fragment execute +/* Dijkstra's algorithm for the single-source shortest path problem + * in an undirected graph whose edges have non-negative integer weights. */ +public class Dijkstra { + /** Infinity value. */ + protected static final Integer INFINITE = Integer.MAX_VALUE; + /** Input graph. */ + protected Graph graph; + /** Decoration key for edge weights */ + protected Object WEIGHT; + /** Decoration key for vertex distances */ + protected Object DIST = new Object(); + /** Decoration key for entries in the priority queue */ + protected Object ENTRY = new Object(); + /** Auxiliary priority queue. */ + protected AdaptablePriorityQueue> Q; + /** Executes Dijkstra's algorithm. + * @param g Input graph + * @param s Source vertex + * @param w Weight decoration object */ + public void execute(Graph g, Vertex s, Object w) throws InvalidKeyException{ + graph = g; + WEIGHT = w; + DefaultComparator dc = new DefaultComparator(); + Q = new HeapAdaptablePriorityQueue>(dc); + dijkstraVisit(s); + } + /** Get the distance of a vertex from the source vertex. +//end#fragment execute + * This method returns the length of a shortest path from the source + * to u after {@link #execute(Graph,Vertex,Object) execute} + * has been called. +//begin#fragment execute + * @param u Start vertex for the shortest path tree */ + public int getDist(Vertex u) throws InvalidKeyException{ + return (Integer) u.get(DIST); + } +//end#fragment execute + + //begin#fragment dijkstraVisit + /** The actual execution of Dijkstra's algorithm. + * @param v source vertex. + */ + protected void dijkstraVisit (Vertex v) throws InvalidKeyException{ + // store all the vertices in priority queue Q + for (Vertex u: graph.vertices()) { + int u_dist; + if (u==v) + u_dist = 0; + else + u_dist = INFINITE; + Entry> u_entry = Q.insert(u_dist, u); // autoboxing + u.put(ENTRY, u_entry); + } + // grow the cloud, one vertex at a time + while (!Q.isEmpty()) { + // remove from Q and insert into cloud a vertex with minimum distance + Entry> u_entry = Q.min(); + Vertex u = u_entry.getValue(); + int u_dist = u_entry.getKey(); + Q.remove(u_entry); // remove u from the priority queue + u.put(DIST,u_dist); // the distance of u is final + u.remove(ENTRY); // remove the entry decoration of u + if (u_dist == INFINITE) + continue; // unreachable vertices are not processed + // examine all the neighbors of u and update their distances + for (Edge e: graph.incidentEdges(u)) { + Vertex z = graph.opposite(u,e); + Entry> z_entry + = (Entry>) z.get(ENTRY); + if (z_entry != null) { // check that z is in Q, i.e., not in the cloud + int e_weight = (Integer) e.get(WEIGHT); + int z_dist = z_entry.getKey(); + if ( u_dist + e_weight < z_dist ) // relaxation of edge e = (u,z) + Q.replaceKey(z_entry, u_dist + e_weight); + } + } + } + } + //end#fragment dijkstraVisit +} // end of Dijkstra class diff --git a/graph/FindCycleDFS.java b/graph/FindCycleDFS.java new file mode 100644 index 0000000..c7cbd25 --- /dev/null +++ b/graph/FindCycleDFS.java @@ -0,0 +1,58 @@ +package graph; +//begin#fragment FindCycleDFS + +import position.NodePositionList; +import position.Position; +import position.PositionList; + +/** This class specializes DFS to find a cycle. */ +//end#fragment FindCycleDFS + /* @author Roberto Tamassia, Michael Goodrich, Eric Zamore + */ +//begin#fragment FindCycleDFS +public class FindCycleDFS + extends DFS> { + protected PositionList cycle; // sequence of edges of the cycle + protected boolean done; + protected Vertex cycleStart; +//end#fragment FindCycleDFS + + /** + * Executes the DFS algorithm. + * @return collection containing the vertices and + * edges of a cycle. + */ +//begin#fragment FindCycleDFS + public void setup() { + cycle = new NodePositionList(); + done = false; + } + protected void startVisit(Vertex v) { cycle.addLast(v); } + protected void finishVisit(Vertex v) { + cycle.remove(cycle.last()); // remove v from cycle + if (!cycle.isEmpty()) cycle.remove(cycle.last()); // remove edge into v from cycle + } + protected void traverseDiscovery(Edge e, Vertex from) { + cycle.addLast(e); + } + protected void traverseBack(Edge e, Vertex from) { + cycle.addLast(e); // back edge e creates a cycle + cycleStart = graph.opposite(from, e); + cycle.addLast(cycleStart); // first vertex completes the cycle + done = true; + } + protected boolean isDone() { return done; } + public Iterable finalResult(Iterable r) { + // remove the vertices and edges from start to cycleStart + if (!cycle.isEmpty()) { + for (Position p: cycle.positions()) { + if (p.element() == cycleStart) + break; + cycle.remove(p); // remove vertex from cycle + } + } + return cycle; // list of the vertices and edges of the cycle + } +} +//end#fragment FindCycleDFS + diff --git a/graph/FindPathDFS.java b/graph/FindPathDFS.java new file mode 100644 index 0000000..deeed5d --- /dev/null +++ b/graph/FindPathDFS.java @@ -0,0 +1,43 @@ +package graph; + +//begin#fragment FindPathDFS + +import position.NodePositionList; +import position.Position; +import position.PositionList; + +/** Class specializing DFS to find a path between a start vertex and a target + * vertex. It assumes the target vertex is passed as the info object to the + * execute method. It returns an iterable list of the vertices and edges + * comprising the path from start to info. The returned path is empty if + * info is unreachable from start. */ +public class FindPathDFS + extends DFS, Iterable> { + protected PositionList path; + protected boolean done; + /** Setup method to initialize the path. */ + public void setup() { + path = new NodePositionList(); + done = false; + } + protected void startVisit(Vertex v) { + path.addLast(v); // add vertex v to path + if (v == info) + done = true; + } + protected void finishVisit(Vertex v) { + path.remove(path.last()); // remove v from path + if(!path.isEmpty()) // if v is not the start vertex + path.remove(path.last()); // remove discovery edge into v from path + } + protected void traverseDiscovery(Edge e, Vertex from) { + path.addLast(e); // add edge e to the path + } + protected boolean isDone() { + return done; + } + public Iterable finalResult(Iterable r) { + return path; + } +} +//end#fragment FindPathDFS diff --git a/graph/Kruskal.java b/graph/Kruskal.java new file mode 100644 index 0000000..d7594bf --- /dev/null +++ b/graph/Kruskal.java @@ -0,0 +1,52 @@ +package graph; + + +import partition.ListPartition; +import partition.Partition; +import position.NodePositionList; +import position.PositionList; +import priorityqueue.Entry; +import priorityqueue.PriorityQueue; +import priorityqueue.heap.HeapPriorityQueue; + +import java.security.InvalidKeyException; + +public class Kruskal { + protected Graph graph; + protected Object WEIGHT; + protected PositionList> EList; + + protected PriorityQueue> Q; + + public Iterable> execute(Graph g, Object w) throws InvalidKeyException{ + graph = g; + WEIGHT = w; + Q = new HeapPriorityQueue>(); + EList = new NodePositionList>(); + KruskalAlg(); + return EList; + } + + protected void KruskalAlg() throws InvalidKeyException{ + Partition> P = new ListPartition>(); + + for (Vertex w : graph.vertices()) + P.makeSet(w); + + for (Edge e : graph.edges()) + Q.insert((Double) e.get(WEIGHT), e); + + while (EList.size() < graph.numVertices() - 1) { + Entry> e_entry = Q.removeMin(); + Edge e = e_entry.getValue(); + Vertex endV[] = graph.endVertices(e); + Vertex u = endV[0]; + Vertex v = endV[1]; + + if (P.find(u) != P.find(v)) { + P.union(P.find(u), P.find(v)); + EList.addLast(e); + } + } + } +} diff --git a/graph/gioel.rar b/graph/gioel.rar new file mode 100644 index 0000000000000000000000000000000000000000..39111f19299e246de843a46f0e77072198184d8c GIT binary patch literal 8234 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