1/* 2 * Graph.java3 * 4 * Computer Science S-22, Harvard University5 */6 7import java.io.*;8import java.util.*;9 10/**11 * An implementation of a Graph ADT.12 */13public class Graph {14 /*15 * Vertex - a private inner class for representing a vertex.16 */17 private class Vertex {18 private String id;19 private Edge edges; // adjacency list, sorted by edge cost20 private Vertex next; // next Vertex in linked list21 private boolean encountered;22 private boolean done;23 private Vertex parent; // preceding vertex in path from root24 private double cost; // cost of shortest known path25 26 private Vertex(String id) {27 this.id = id;28 cost = Double.POSITIVE_INFINITY;29 }30 31 /*32 * reinit - reset the starting values of the fields used by33 * the various algorithms, removing any values set by previous34 * uses of the algorithms.35 */36 private void reinit() {37 done = false;38 encountered = false;39 parent = null;40 cost = Double.POSITIVE_INFINITY;41 }42 43 /*44 * addToAdjacencyList - add the specified edge to the45 * adjacency list for this vertex.46 */47 private void addToAdjacencyList(Edge e) {48 /* Add to the front of the list. */49 e.next = edges;50 edges = e;51 }52 53 /*54 * pathString - returns a string that specifies the path from55 * the root of the current spanning tree (if there is one) to56 * this vertex. If this method is called after performing57 * Dijkstra's algorithm, the returned string will specify the58 * shortest path.59 */60 private String pathString() {61 String str;62 63 if (parent == null) {64 str = id; /* base case: this vertex is the root */65 } else {66 str = parent.pathString() + " -> " + id;67 }68 69 return str;70 }71 72 /*73 * toString - returns a string representation of the vertex 74 * of the following form:75 * vertex v:76 * edge to vi (cost = c1i)77 * edge to vj (cost = c1j)78 * ...79 */80 public String toString() {81 String str = "vertex " + id + ":\n";82 83 /* 84 * Iterate over the edges, adding a line to the string for85 * each of them.86 */87 Edge e = edges;88 while (e != null) {89 // Note: we have to use just the id field of the 90 // end vertex, or else we'll get infinite recursion!91 str += "\tedge to " + e.end.id;92 str += " (cost = " + e.cost + ")\n"; 93 94 e = e.next;95 }96 97 return str;98 }99 }100 101 /*102 * Edge - a private inner class for representing an edge.103 */104 private class Edge {105 private Vertex start;106 private Vertex end;107 private double cost;108 private Edge next; // next Edge in adjacency list109 110 private Edge(Vertex start, Vertex end, double cost) {111 this.start = start;112 this.end = end;113 this.cost = cost;114 }115 }116 117 /******* Graph instance variables and method start here. *********/118 119 /* A linked list of the vertices in the graph. */120 private Vertex vertices;121 122 /*123 * reinitVertices - private helper method that resets the starting124 * state of all of the vertices in the graph, removing any values125 * set by previous uses of the algorithms.126 */127 private void reinitVertices() {128 Vertex v = vertices;129 while (v != null) {130 v.reinit();131 v = v.next;132 }133 }134 135 /*136 * getVertex - private helper method that returns a reference to137 * the vertex with the specified id. If the vertex isn't138 * in the graph, it returns null.139 */140 private Vertex getVertex(String id) {141 Vertex v = vertices;142 143 while (v != null && !v.id.equals(id)) {144 v = v.next;145 }146 147 return v;148 }149 150 /*151 * addVertex - private helper method that adds a vertex with152 * the specified id and returns a reference to it.153 */154 private Vertex addVertex(String id) {155 Vertex v = new Vertex(id);156 157 /* Add to the front of the list. */158 v.next = vertices;159 vertices = v;160 161 return v;162 }163 164 /**165 * addEdge - add an edge with the specified cost, and with start166 * and end vertices that have the specified IDs. The edge will167 * be stored in the adjacency list of the start vertex. If a168 * specified vertex isn't already part of the graph, it will be169 * added, too.170 */171 public void addEdge(String startVertexID, String endVertexID, double cost) {172 Vertex start = getVertex(startVertexID);173 if (start == null) {174 start = addVertex(startVertexID);175 }176 177 Vertex end = getVertex(endVertexID);178 if (end == null) {179 end = addVertex(endVertexID);180 }181 182 Edge e = new Edge(start, end, cost);183 start.addToAdjacencyList(e);184 }185 186 /**187 * toString - returns a concatenation of the string188 * representations of all of the vertices in the graph.189 */190 public String toString() {191 String str = "";192 193 Vertex v = vertices;194 while (v != null) {195 str += v;196 v = v.next;197 }198 199 return str;200 }201 202 /**203 * initFromFile - initialize a graph using the information in the204 * specified file. The file should be a text file consisting of205 * lines that specify the edges of the graph in the following form:206 * <start vertex data> <end vertex data> <cost>207 */208 public void initFromFile(String fileName) {209 String lineString = "";210 211 try {212 /* This Scanner will scan the file, one line at a time. */213 Scanner file = new Scanner(new File(fileName));214 215 /* Parse the file, one line at a time. */216 while (file.hasNextLine()) {217 lineString = file.nextLine();218 Scanner line = new Scanner(lineString);219 220 String startID = line.next();221 String endID = line.next();222 double cost = line.nextDouble();223 addEdge(startID, endID, cost);224 }225 } catch (IOException e) {226 System.out.println("Error accessing " + fileName);227 System.exit(1);228 } catch (NoSuchElementException e) {229 System.out.println("invalid input line: " + lineString);230 System.exit(1);231 }232 }233 234 /**235 * depthFirstTrav - perform a depth-first traversal starting from236 * the vertex with the specified ID. After making sure that the237 * start vertex exists, it makes an initial call to the recursive238 * method dfTrav, which does the actual traversal.239 */240 public void depthFirstTrav(String originID) {241 reinitVertices();242 243 /* Get the specified start vertex. */244 Vertex start = getVertex(originID);245 if (start == null) {246 throw new IllegalArgumentException("no such vertex: " + originID);247 }248 249 /* Start the recursion rolling... */250 dfTrav(start, null);251 }252 253 /*254 * dfTrav - a recursive method used to perform a depth-first255 * traversal, starting from the specified vertex v. The parent256 * parameter specifies v's parent in the depth-first spanning257 * tree. In the initial invocation, the value of parent should be258 * null, because the starting vertex is the root of the spanning259 * tree.260 */261 private static void dfTrav(Vertex v, Vertex parent) {262 /* Visit v. */263 System.out.println(v.id);264 v.done = true;265 v.parent = parent;266 267 Edge e = v.edges;268 while (e != null) {269 Vertex w = e.end;270 if (!w.done) {271 dfTrav(w, v);272 }273 e = e.next;274 }275 }276 277 /**278 * breadthFirstTrav - perform a breadth-first traversal starting from279 * the vertex with the specified ID.280 */281 public void breadthFirstTrav(String originID) {282 reinitVertices();283 284 /* Get the specified start vertex. */285 Vertex origin = getVertex(originID);286 if (origin == null) {287 throw new IllegalArgumentException("no such vertex: " + originID);288 }289 290 bfTrav(origin);291 }292 293 private static void bfTrav(Vertex origin) {294 /* Mark the origin as encountered, and add it to the queue. */295 origin.encountered = true;296 origin.parent = null;297 Queue<Vertex> q = new LLQueue<Vertex>();298 q.insert(origin);299 300 while (!q.isEmpty()) {301 /* Remove a vertex v and visit it. */302 Vertex v = q.remove();303 System.out.println(v.id);304 305 /* Add v's unencountered neighbors to the queue. */306 Edge e = v.edges;307 while (e != null) {308 Vertex w = e.end;309 if (!w.encountered) {310 w.encountered = true;311 w.parent = v;312 q.insert(w);313 }314 e = e.next;315 }316 }317 }318 319 /**320 * dijkstra - apply Dijkstra's algorithm starting from the321 * specified origin vertex to find the shortest path from the322 * origin to all other vertices that can be reached from the323 * origin.324 *325 * The method prints the vertices in the order in which they are326 * finalized. For each vertex v, it lists the total cost of the327 * shortest path from the origin to v, as well as v's parent328 * vertex. Tracing back along the parents gives the shortest329 * path.330 */331 public void dijkstra(String originID) {332 /* This will give all vertices an infinite cost. */333 reinitVertices();334 335 /* Get the origin and set its cost to 0. */336 Vertex origin = getVertex(originID);337 if (origin == null) {338 throw new IllegalArgumentException("no such vertex: " + originID);339 }340 origin.cost = 0;341 342 while (true) {343 /* Find the unfinalized vertex with the minimal cost. */344 Vertex w = null;345 Vertex v = vertices;346 while (v != null) {347 if (!v.done && (w == null || v.cost < w.cost)) {348 w = v;349 }350 v = v.next;351 }352 353 /* 354 * If there are no unfinalized vertices, or if all of the355 * unfinalized vertices are unreachable from the origin356 * (which is the case if the w.cost is infinite), then357 * we're done.358 */359 if (w == null || w.cost == Double.POSITIVE_INFINITY) {360 return;361 }362 363 /* Finalize w. */364 System.out.println("\tfinalizing " + w.id + " (cost = " + w.cost +365 (w.parent == null ? ")" : ", parent = " + w.parent.id + ")"));366 System.out.println("\t\tpath = " + w.pathString());367 w.done = true;368 369 /* Try to improve the estimates of w's unfinalized neighbors. */370 Edge e = w.edges;371 while (e != null) {372 Vertex x = e.end;373 if (!x.done) {374 double cost_via_w = w.cost + e.cost;375 if (cost_via_w < x.cost) {376 x.cost = cost_via_w;377 x.parent = w;378 }379 }380 e = e.next;381 }382 }383 }384 385 /**386 * prim - apply Prim's algorithm starting from the specified387 * vertex to find a minimum spanning tree for the graph. 388 * The method assumes that the graph is connected.389 *390 * The "done" instance variable is used to divide the vertices391 * into two sets. Initially, the origin is in one set (the one392 * containing vertices for which done == true), and all other393 * vertices are in a second set (the one containing vertices for394 * which done == false). We repeatedly add the lowest-cost edge395 * joining a vertex in the first set to a vertex in the second396 * set, and then we move the vertex in the second set to the first397 * set by setting its done field to true.398 */399 public void prim(String originID) {400 reinitVertices();401 402 /* Get the origin and mark it as done. */403 Vertex origin = getVertex(originID);404 if (origin == null) {405 throw new IllegalArgumentException("no such vertex: " + originID);406 }407 origin.done = true;408 409 while (true) {410 /* 411 * Find the minimal-cost edge linking a done vertex with412 * one that isn't done. 413 */414 Edge edgeToAdd = null;415 Vertex v = vertices;416 while (v != null) {417 if (v.done) {418 Edge e = v.edges;419 while (e != null) {420 if (!e.end.done && 421 (edgeToAdd == null || e.cost < edgeToAdd.cost)) {422 edgeToAdd = e;423 }424 e = e.next;425 }426 }427 v = v.next;428 }429 430 /* 431 * If no such edge exists, we're done.432 */433 if (edgeToAdd == null) {434 return;435 }436 437 /* Add the edge and mark its end vertex done. */438 System.out.println("\tadding edge (" + edgeToAdd.start.id + ", " +439 edgeToAdd.end.id + ")");440 edgeToAdd.end.parent = edgeToAdd.start;441 edgeToAdd.end.done = true;442 }443 }444 445 public static void main(String[] args) {446 Scanner in = new Scanner(System.in);447 Graph g = new Graph();448 449 System.out.print("Graph info file: ");450 String fileName = in.nextLine();451 g.initFromFile(fileName);452 453 System.out.print("starting point: ");454 String start = in.nextLine();455 456 System.out.println("depth-first traversal from " + start + ":");457 g.depthFirstTrav(start);458 459 System.out.println("breadth-first traversal from " + start + ":");460 g.breadthFirstTrav(start);461 462 System.out.println("Dijkstra's algorithm from " + start + ":");463 g.dijkstra(start);464 465 System.out.println("Prim's algorithm from " + start + ":");466 g.prim(start);467 }468}
Vertex vertices
Field defined at line 120.
reinitVertices()
Method defined at line 127.
getVertex(id)
Method defined at line 140.
addVertex(id)
Method defined at line 154.
addEdge(startVertexID, endVertexID, cost)
Method defined at line 171.
toString()
Method defined at line 190.
initFromFile(fileName)
Method defined at line 208.
depthFirstTrav(originID)
Method defined at line 240.
dfTrav(v, parent)
Method defined at line 261.
breadthFirstTrav(originID)
Method defined at line 281.
bfTrav(origin)
Method defined at line 293.
dijkstra(originID)
Method defined at line 331.
prim(originID)
Method defined at line 399.
main(args)
Method defined at line 445.