Graph.java

Download
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}