Your goal for this part of the assignment is to develop an efficient implementation for a directed, unweighted, graph ADT.
The following table indicates the methods in the graph ADT and the required efficiency for each.
Method Name | Running Time (Average Case) |
---|---|
addNode(T id) | \(O(1)\) |
addEdge(T from, T to) | \(O(1)\) |
hasEdge(T from, T to) | \(O(1)\) |
removeNode(T id) |
\(O(i + o)\) (Where \(i\) is the in-degree and \(o\) is the out-degree of the removed node.) |
removeEdge(T from, T to) | \(O(1)\) |
neighbors(T id) | \(O(1)\) |
allNodes() | \(O(1)\) |
numNodes() | \(O(1)\) |
numEdges() | \(O(1)\) |
Here is some sample code illustrating how this Graph ADT may be used:
// Create a graph using Strings as node labels
Graph<String> graph1 = new HashGraph<>();
graph1.addNode("A"); // The node A is added
graph1.addEdge("B", "A"); // The node B is be added,
// and the edge B -> A
System.out.println(graph1.hasEdge("A", "B")); // Prints false
System.out.println(graph1.hasEdge("B", "A")); // Prints true
// Make a fully connected graph with 10 nodes...
Graph<Integer> graph2 = new HashGraph<>();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
graph2.addEdge(i, j);
}
}
// This should print 10
System.out.println(graph2.numNodes());
// This should print 90 (no self links are allowed)
System.out.println(graph2.numEdges());
Our textbook describes two basic data structures for implementing a Graph ADT: adjacency matrix and adjacency list. An adjacency matrix is not a good choice for this application because it doesn't provide a good mechanism for dynamically adding and removing nodes.
An adjacency list is a more promising approach, but there are some efficiency issues with a simple implementation using an array of singly linked lists. For one, determining whether two nodes are neighbors may require a \(\Theta(|V|)\) sequential search through a node's neighbor list. Checking and modifying edges can be implemented more efficiently if the adjacency "List" is actually stored as a hash-table-backed Set.
Note that you are not required to provide your own hashing
implementation for this project. You are welcome to use the HashMap
and HashSet
classes from the Java Collections Framework.
Consider the problem of removing node A from the graph pictured below:
Efficiently removing the edge (A, E) is straightforward. However, a correct implementation will also need to remove the edges (B, A), (C, A) and (D, A). This could by done be checking each node in the graph to find those that are connected to A. Unfortunately, this approach would require \(\Omega(|V|)\) operations. An efficient implementation will require a mechanism for quickly finding both outgoing and incoming edges for any node.
Complete the unfinished method in GraphUtils.java.
The OpenDSA textbook contains implementations of both BFS traversal and topological sort. You are welcome to use these implementations as a starting point for developing your solutions to Part 2.
A BFS traversal is useful for finding shortest paths in an unweighted graph: BFS will visit all nodes within \(n\) steps of the starting node before visiting any nodes that are \(n + 1\) steps away. This means that as soon as the traversal visits a node, we are guaranteed that we have discovered the shortest path to that node. The challenge is in reconstructing the path. This can be accomplished by maintaining a record of the predecessor of each node in the traversal. Once the goal node is reached, we can then reconstruct the entire path by stepping backward through the predecessors until we reach the starting node.
Here is a simple GUI application that reads in a graph specification from a file and displays the shortest path between any two nodes.
The file states.txt contains a graph where each node represents a U.S. state, and two nodes are connected if the states share a common border.
For Part 1, submit only HashGraph.java
. For Part 2 submit only
GraphUtils.java
.
The project will be graded as follows:
Autolab Functionality Tests For Part 1 | 70% |
Autolab Efficiency Tests For Part 1 | 10% |
Autolab Functionality Tests For Part 2 | 10% |
Autolab Style Checks | 10% |
Note that it will not be possible to receive any credit for the efficiency tests for Part 1 if your submission fails any of the Part 1 functionality tests.