AdjacencyLists Representation
The standard representation that is preferred for graphs that are not dense is called the adjacencylists representation, where we keep track of all the vertices connected to each vertex on a linked list that is associated with that vertex. We maintain an array of lists so that, given a vertex, we can immediately access its list; we use linked lists so that we can add new edges in constant time. Program 17.9 is an implementation of the ADT interface in Program 17.1 that is based on this approach, and Screenshot depicts an example. To add an edge connecting v and w to this representation of the graph, we add w to v's adjacency list and v to w's adjacency list. In this way, we still can add new edges in constant time, but the total amount of space that we use is proportional to the number of vertices plus the number of edges (as opposed to the number of vertices squared, for the adjacencymatrix representation). For undirected graphs, we again represent each edge in two different places: an edge connecting v and w is represented as nodes on both adjacency lists. It is important to include both; otherwise, we could not answer efficiently simple questions such as, "Which vertices are adjacent to vertex v?" Program 17.10 implements the iterator that answers this question for clients, in time proportional to the number of such vertices.
Screenshot Adjacencylists data structure
This figure depicts a representation of the graph in Screenshot as an array of linked lists. The space used is proportional to the number of nodes plus the number of edges. To find the indices of the vertices connected to a given vertex v, we look at the vth position in an array, which contains a pointer to a linked list containing one node for each vertex connected to v. The order in which the nodes appear on the lists depends on the method that we use to construct the lists.
The implementation in Programs 17.9 and 17.10 is a lowlevel one. An alternative is to use a Java collection class to implement each edge list (see Exercise 17.30). The disadvantage of doing so is that such implementations typically support many more operations than we need and therefore typically carry extra overhead that might affect the performance of all of our algorithms (see Exercise 17.31). Indeed, all of our graph algorithms use the Graph ADT interface, so this implementation is an appropriate place to encapuslate all the lowlevel operations and concentrate on efficiency without affecting our other code. Another advantage of using the linkedlist representation is that it provides a concrete basis for understanding the performance characteristics of our implementations.
Graph ADT implementation (adjacency lists)
This implementation of the interface in Program 17.1 uses an array of linked lists, one corresponding to each vertex. It is equivalent to the representation of Program 3.17, where an edge vw is represented by a node for w on list v and a node for v on list w. Implementations of remove and edge are omitted, because our use of singlylinked lists does not accommodate constanttime implementations of these operations (adding them for clients that do not need fast implementations is a straightforward exercise (see Exercise 17.28)). The insert code keeps insertion time constant by not checking for duplicate edges, and the total amount of space used is proportional to V + E. This representation is most suitable for sparse multigraphs.
class Graph // sparse multigraph implementation
{
private int Vcnt, Ecnt;
private boolean digraph;
private class Node
{ int v; Node next;
Node(int x, Node t) { v = x; next = t; }
}
private Node adj[];
Graph(int V, boolean flag)
{
Vcnt = V; Ecnt = 0; digraph = flag;
adj = new Node[V];
}
int V() { return Vcnt; }
int E() { return Ecnt; }
boolean directed() { return digraph; }
void insert(Edge e)
{ int v = e.v, w = e.w;
adj[v] = new Node(w, adj[v]);
if (!digraph) adj[w] = new Node(v, adj[w]);
Ecnt++;
}
AdjList getAdjList(int v)
// Program 17.10
}

Iterator for adjacencylists representation
This implementation of the iterator for Program 17.9 maintains a link t to traverse the linked list associated with vertex v. An invocation of beg() followed by a sequence of invocations of nxt() (checking that end() is false before each invocation) gives a sequence of the vertices adjacent to v in G.
AdjList getAdjList(int v)
{ return new AdjLinkedList(v); }
private class AdjLinkedList implements AdjList
{
private int v;
private Node t;
AdjLinkedList(int v)
{ this.v = v; t = null; }
public int beg()
{ t = adj[v];
return t == null ? 1 : t.v; }
public int nxt()
{ if (t != null) t = t.next;
return t == null ? 1 : t.v; }
public boolean end()
{ return t == null; }
}

As for our arraybased implementation, the linkedlist–based implementation in Programs 17.9 and 17.10 lacks a clone implementation (see Section 4.9). Such a method is easy to develop by extending the one for the clonable queue implementation of Program 4.24 (see Exercise 17.29). By contrast to Program 17.7, Program 17.9 builds multigraphs, because it does not remove parallel edges. Checking for duplicate edges in the adjacencylists structure would necessitate searching through the lists and could take time proportional to V. Similarly, Program 17.9 does not include an implementation of the remove edge operation or the edge existence test. Adding implementations for these methods is an easy exercise (see Exercise 17.28), but each operation might take time proportional to V to search through the lists for the nodes that represent the edges. These costs make the basic adjacencylists representation unsuitable for apps involving either huge graphs where parallel edges cannot be tolerated or heavy use of remove edge or of edge existence tests. In , we discuss adjacencylist implementations that support constanttime remove edge and edge existence operations. When a graph's vertex names are not integers, then (as with adjacency matrices) two different programs might associate vertex names with the integers from 0 to V – 1 in two different ways, leading to two different adjacencylist structures (see, for example, Program 17.15). We cannot expect to be able to tell whether two different structures represent the same graph because of the difficulty of the graph isomorphism problem. Moreover, with adjacency lists, there are numerous representations of a given graph even for a given vertex numbering. No matter in what order the edges appear on the adjacency lists, the adjacencylist structure represents the same graph (see Exercise 17.33). This characteristic of adjacency lists is important to know because the order in which edges appear on the lists affects, in turn, the order in which edges are processed by algorithms. That is, the adjacencylist structure determines how our various algorithms see the graph. An algorithm should produce a correct answer no matter how the edges are ordered on the adjacency lists, but it might get to that answer by different sequences of computations for different orderings. If an algorithm does not need to examine all the graph's edges, this effect might affect the time that it takes. And, if there is more than one correct answer, different input orderings might lead to different output results. The primary advantage of the adjacencylists representation over the adjacencymatrix representation is that it always uses space proportional to E + V, as opposed to V^{2} in the adjacency matrix. The primary disadvantage is that testing for the existence of specific edges can take time proportional to V, as opposed to constant time in the adjacency matrix. These differences trace, essentially, to the difference between using a linked list and using an array to represent the set of vertices incident on each vertex. Thus, we see again that an understanding of the basic properties of linked data structures and array is critical if we are to develop efficient graph ADT implementations. Our interest in these performance differences is that we want to avoid implementations that are inappropriately inefficient under unexpected circumstances when a wide range of operations is to be demanded of the ADT. In , we discuss the app of basic data structures to realize many of the theoretical benefits of both structures. Nonetheless, Program 17.9 is a simple implementation with the essential characteristics that we need to learn efficient algorithms for processing sparse graphs.
Exercises
17.27 Show, in the style of Screenshot, the adjacencylists structure produced when you use Program 17.9 to insert the edges in the graph
(in that order) into an initially empty graph.
Provide implementations of remove and edge for the adjacencylists graph class (Program 17.9). Note: Duplicates may be present, but it suffices to remove any edge connecting the specified vertices.
Add a clone method to the adjacencylists graph class (Program 17.9).
17.30 Modify the adjacencylists implementation of Programs 17.9 and 17.10 to use a Java collection instead of an explicit linked list for each adjacency list.
Run empirical tests to compare your Graph implementation of Exercise 17.30 with the implementation in the text. For a wellchosen set of values for V, compare running times for a client program that builds complete graphs with V vertices, then extracts the edges using Program 17.2.
17.32 Give a simple example of an adjacencylists graph representation that could not have been built by repeated insertion of edges by Program 17.9.
How many different adjacencylists representations represent the same graph as the one depicted in Screenshot?
17.34 Add a method to the graph ADT (Program 17.1) that removes selfloops and parallel edges. Provide the trivial implementation of this method for the adjacencymatrix–based class (Program 17.7), and provide an implementation of the method for the adjacencylist–based class (Program 17.9) that uses time proportional to E and extra space proportional to V.
Write a version of Program 17.9 that disallows parallel edges (by scanning through the adjacency list to avoid adding a duplicate entry on each edge insertion) and selfloops. Compare your implementation with the implementation described in Exercise 17.34. Which is better for static graphs? Note: See Exercise 17.49 for an efficient implementation.
Write a client of the graph ADT that returns the result of removing selfloops, parallel edges, and degree0 (isolated) vertices from a given graph. Note: The running time of your program should be linear in the size of the graph representation.
• 17.37 Write a client of the graph ADT that returns the result of removing selfloops, collapsing paths that consist solely of degree2 vertices from a given graph. Specifically, every degree2 vertex in a graph with no parallel edges appears on some path u...w where u and w are either equal or not of degree 2. Replace any such path with uw, and then remove all unused degree2 vertices as in Exercise 17.36. Note: This operation may introduce selfloops and parallel edges, but it preserves the degrees of vertices that are not removed.
17.38 Give a (multi)graph that could result from applying the transformation described in Exercise 17.37 on the sample graph in Screenshot.
