Gettysburg College

CS 216
Data Structures

Spring 2024

Assignment 10

Due: Mon, Apr 22, by 11:59pm
  • method stubs: Vertex|Edge|Graph|GraphAlgos.java
  • junit tests: GraphAlgosTest.java
  • coverage-Graph.pdf
  • coverage-GraphAlgos.pdf
  • api-Graph.pdf (ony for Graph.java)
Due: Thu, Apr 25, by 11:59pm
  • Vertex|Edge|Graph|GraphAlgos.java
  • VertexComparator.java
  • GraphAlgosTest.java
  • run-jar.png (screenshot of terminal)
  • coverage-Graph.pdf
  • coverage-GraphAlgos.pdf

Description

This assignment is part of the sequence on Graphs and Graph algorithms. A Graph is a collection of Vertices connected by Edges. It is a generalization of the Tree data structure from the previous assignments.

The focus is on the following algorithms and data structures:

Create a project called Graph with the required classes.

Visualization resources

Here is pseudocode for the various algorithms:

Graph Algorithms

Here is a useful visualization resource:

JUnit

Put the test cases in class GraphAlgosTest. The JUnit testing will be different, since the methods will only print to the screen.

This means that there won't be assertEquals. Instead each @Test method will look like a mini-main method and you will inspect the console to see if the output is correct.

[ To test individual components you can comment //@Test ]

Here is an example:

@Test
public void test_bfs()
{
    Graph graph = new Graph("undirected.txt");

    String[] labelsList = { "a", ... };

    for ( ... ) {
        System.out.println("Source: ?");
        GraphAlgos.bfs(graph, ?);
    }
}
@Test
public void test_dijkstra()
{
    Graph graph = new Graph("directed.txt");

    String[] labelsList = { "a", ... };

    for ( ... ) {
        System.out.println("Source: ?");
        GraphAlgos.dijkstra(graph, ?);
    }
}
@Test
public void test_dfs()
{
    Graph graph = new Graph("directed.txt");

    String[] labelsList = { "a", ... };

    for ( ... ) {
        System.out.println("Source: ?");
        GraphAlgos.dfs(graph, ?);
    }
}

Vertex Representation

A vertex in the graph will represent the source or target of an edge. It will also store information that is needed for bookkeeping in the various algorithms - these could be left as default (i.e. non-private) as in the previous Node classes:

Each vertex will have the following data members:

Each vertex will have the following constructor and method:

Edge Representation

An Edge is described by its source and target vertices, and by its weight.

The interface for the Edge class is:

Graph Representation

The Graph should use the adjacency list representation based on a HashMap with (key=Vertex_Label, value=List_of_Edges).

The graph data members:

Here is the required interface for the Graph class:

Graph(String filename)

Reads a graph from the file with the given filename (see Scanner API).

Make sure to use the appropriate constructor to open the file. If Eclipse does not suggest adding try/catch, you are using the wrong Scanner constructor.

Methods .next() and .nextDouble() will read a single string and a single double, respectively. Method hasNext() will determine when to stop.

The files are formatted as follows:

source   target   weight
Test files are available here - save them in the main project folder (next to src/, bin/):
London Tube Map
Graph()

This constructor is not required, but you might use it initially if you are not sure whether you can read from a file.

This is just a sequence of addEgde calls for each line in the file you want to test.

public Graph()
{
    // any intialization

    // for BFS load "undirected.txt"
    //    addEdge("a", "b", 2);
    //    addEdge("a", "f", 7);
    //    addEdge("a", "g", 3);
    //    addEdge("a", "b", 2);
    ...

    USE ONLY ONE OR THE OTHER

    // for Dijkstra/DFS load "directed.txt"
    //    addEdge("a", "b", 4);
    //    addEdge("a", "f", 2);
    //    addEdge("b", "c", 3);
    ....
}
void addEdge(String sourceLabel, String targetLabel, double weight)

Adds an edge with the given weight from the vertex with the given source label to the vertex with the given target label.

Make sure duplicate vertices are not created. In other words, if called multiple times with sourceLabel or targetLabel "Gettysburg" it should always use the same Vertex object for "Gettysburg".

Vertex getVertex(String label)

Returns a Vertex object for the given label.

It should ensure that vertices are constructed/setup properly.

List<Edge> getAdjacent(Vertex source)

Returns an unmodifiable list of the edges that have the given vertex as their source.

[ Initially ignore the unmodifiable part. Later see the relevant method in the Collections API. ]

Collection<Vertex> getVertices()

Returns an unmodifiable collection of the vertices in the graph.

[ Initially ignore the unmodifiable part. Later see the relevant method in the Collections API. ]

Graph Traversal (BFS, DFS)

Create class GraphAlgos and implement the Breadth-First Search and Depth-First Search graph traversal algorithms by adding the following methods:

static void __method__(Graph graph)

A useful helper method...
static void bfs(Graph graph, String sourceLabel)

Performs Breadth-First Search (pseudocode) on the given graph starting at the given source vertex. This is essentially Level Order with vertex marking; see Figure 4.3 in [ALG].

The vertices are displayed on one line separated by a space in the order they are finished in the format:

a:0:* b:1:a f:1:a g:1:a c:2:b e:2:f i:2:f h:2:g d:3:c

BFS results on undirected.txt for each vertex as source

  • the label of the vertex
  • the distance to the vertex from the given source in breadth-first order
  • the label of parent vertex, i.e. the one that inserted this vertex in the Queue

To keep track of the edge distance, make the necessary changes to the pseudocode by using the distance field.

static void dfs(Graph graph, String sourceLabel)

Performs Depth-First Search on the given graph starting at the given source vertex. See Figures 3.5 and 3.3 in [ALG].

This method simply calls the next method after carrying out any preliminary steps (if necessary).

The vertices are displayed on one line separated by a space in the order they are finished in the format:

a:0:* b:1:a c:2:b d:3:c e:4:d f:1:a

DFS results on directed.txt for each vertex as source

See BFS for the meaning of the printed components.

static void dfs(Graph graph, Vertex curr)

Uses recursion to perform Depth First Search on the given graph starting at the given vertex curr. See Figures 3.5 and 3.3 in [ALG].

Depth-First Search is a generalization of the Preorder Traversal of BSTree: in each case first "process" the circle and then "process" all arrows for that circle.

The major difference between "peorder traversal" for BSTree and Graph is that in the case of Graph the vertices/nodes need to be marked and the other bookkeeping fields need to be updated (marking is needed to ignore traversing a vertex again after it has been traversed/marked by someone else).

For simplicity, "processing vertex" for Graph will simply print the string representation of the vertex.

Dijkstra's Single-Source Shortest Path

Implement Dijkstra's algorithm for computing the shortest paths from all vertices in a graph to a given source vertex by adding the methods below to the class GraphAlgos.

static void dijkstra(Graph graph, String sourceLabel)

Computes the shortest paths from the given source vertex to all the other vertices in the given graph using Dijkstra's algorithm (pseudocode).

You will need to create VertexComparator.

As soon as a vertex is finished:

  • at first simply display the string representation of that vertex to check that the parent and distance field are correct

  • later change it to display the shortest path forward and backward in the following format (see methods printPath and printPathRec):
    A --4--> C --6--> G --2--> D --5--> F
    F <--5-- D <--2-- G <--6-- C <--4-- A (total length 17)  
    ...
    

    Dijkstra results on directed.txt with c as source

Note: Simply changing the distance field of a vertex will not automatically reconfigure the PQ.

static void printPathLoop(Vertex startVertex, Vertex destVertex)

Uses a loop to print the path (in reverse) from the given destination vertex to the given starting vertex.

[ During the execution of Dijkstra's Algorithm the source will always be given as the starting vertex when this method is called. ]

The path is printed in the following format:

F <--5-- D <--2-- G <--6-- C <--4-- A (total length 17)  
Note: A is the original source and F is the destination, and the path is printed in reverse (this makes the problem simpler).

Note: The individual numbers are edge lengths, not cumulative distances.

This method will be tested indirectly by testing Dijkstra's algorithm.

static void printPathRec(Vertex startVertex, Vertex destVertex)

Same as method printPath but uses recursion (does not include the total length). Unlike the previous method, this one shows the path from the startVertex to the given destVertex, i.e. not in reverse.
A --4--> C --6--> G --2--> D --5--> F
Note: A is the source and F is the destination, and the path is printed correctly. This makes the problem harder (but total distance is not printed).

Note: The individual numbers are edge lengths, not cumulative distances.

This method will be tested indirectly by testing Dijkstra's algorithm.

GraphExplorer - Running from Command Line

Create class GraphExplorer that has only a single main method: Eventually, it should be possible to run the program from the command line as follows:

cd   path/to/project/Graph    (go to correct project folder)
  
THE_FULL_PATH_TO_JAVA  -jar  gbcsmaps.jar  -bfs  undirected.txt  c    (runs BFS starting at c)

THE_FULL_PATH_TO_JAVA  -jar  gbcsmaps.jar  -dfs  directed.txt    d    (runs DFS starting at d)

THE_FULL_PATH_TO_JAVA  -jar  gbcsmaps.jar  -dijkstra  tube.txt   CHARING_CROSS    (runs Dijkstra starting at Charing Cross)
where THE_FULL_PATH_TO_JAVA varies based on the installation; try the following:

terminal