0% found this document useful (0 votes)
105 views5 pages

Inverse Adjacency List in Graphs

Uploaded by

witob23385
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
105 views5 pages

Inverse Adjacency List in Graphs

Uploaded by

witob23385
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

The Graph Abstract Data Type 263

In the remainder of this chapter, we shall refer to a directed graph as a digraph.


When we use the term graph, we assume that it is an undirected graph. Now that we
have defined all the terminology we will need, let us consider the graph as an ADT. The
resulting specification is given in Structure 6.1.

structure Graph is
objects: a nonempty set of vertices and a set of undirected edges, where each edge is a
pair of vertices.
functions:
for all graph g Graph, v, v j, and V2 Vertices
Graph Create() return an empty graph.
Graph InsertVertex(gr6Zp/z, v) return a graph with v inserted.
V has no incident edges.
Graph InsertEdge(grap/2, Vi, V2) return a graph with a new edge
between Vj and
Graph DeleteVertex(grap/z, v) return a graph in which v and all
edges incident to it are removed,
Graph DeleteEdge(grap/z, vi, V2) return a graph in which the edge
(v ], V2) is removed. Leave
the incident nodes in the graph,
Boolean isEmpty(graph) if (graph == empty graph) return
TRUE else return FALSE.
List Adjacent(gra/7/2, v) return a list of all vertices that
are adjacent to v.

Structure 6.1: Abstract data type Graph

The operations in Structure 6.1 are a basic set in that they allow us to create any
arbitrary graph and do some elementary tests. In the later sections of this chapter we
will see functions that traverse a graph (depth first or breadth first search) and that deter­
mine if a graph has special properties (connected, biconnected, planar).

6.1.3 Graph Representations

While several representations for graphs are possible, we shall study only the three most
commonly used; adjacency matrices, adjacency lists, and adjacency multilists.

Adjacency Matrix

Let G = (V, E) be a graph with n vertices, m > 1. The adjacency matrix of G is a two-
dimensional n xn array, say adj-mat. If the edge (Vj, Vj) (<Vi, Vj> for a digraph) is in
264 Graphs

E(G), adj-mat[i][j] = 1. If there is no such edge in E(G), adj~mat[i][j] = 0. The adja­


cency matrices for graphs Gi, G3, and G4 are shown in Figure 6.7. The adjacency
matrix for an undirected graph is symmetric since the edge (v,-, Vj) is in £'(G) iff the edge
(vy, Vj) is also in £(G). In contrast, the adjacency matrix for a digraph need not be sym­
metric. (This is true of G3.) For undirected graphs, we can save space by storing only
the upper or lower triangle of the matrix. (We explored triangular matrices and other
space-saving representations in the exercises of Chapter 2.)

0 1 2 3 0 1 2

0 □ 1 1 1 0 0 1 0
1 1 0 1 1 1 1 0 1
2 1 1 0 1 2 0 0 0
3 1 1 1 0

G ®3
1
0 1 2 3 4 5 6 7

0 0 1 1 0 0 0 0 0
1 1 0 0 10 0 0 0
2 1 0 0 1 0 0 0 0
3 0 1 1 0 0 00 0 0
4 0 0000 1 0 0
5 000 □ 1 0 1 0
6 000 0 0 1 0 1
7 000 0 0 0 1 0

Figure 6.7 : Adjacency matrices for G], G^, and G4

From the adjacency matrix, we can determine easily if there is an edge connecting
any two vertices. Determining the degree of a vertex is also a simple task. For an
undirected graph, the degree of any vertex, i, is its row sum:
n-i

For a directed graph, the row sum is the out-degree, while the column sum is the in­
degree.
Suppose we wish to answering questions such as: How many edges are there in G?
or. Is G connected?. These require us to examine (potentially) all edges of the graph.
Using adjacency matrices, all algorithms that answer these questions require at least
O(n^) time since we must examine - n entries of the matrix (the n diagonal entries
equal zero and can be excluded; only half as many entries need to be examined in the
case of an undirected graph as in this case the adjacency matrix is symmetric) to deter­
mine the edges of the graph. For sparse graphs (i.e., graphs that have a small number of
edges), most of the terms in the adjacency matrix equal zero and we would like to avoid
A
the overhead of examining O(n ) positioins in an adjacency matrix. In fact, we might
The Graph Abstract Data Type 265

expect that the former questions would be answerable in significantly less time, say O(c
+ n) time, where e is the number of edges in G and e « n^/2. For this, we must replace
the adjacency matrix representation with an adjacency list (either sequential or linked)
representation.

Adjacency Lists

In this representation, we replace the n rows of the adjacency matrix with n linked lists,
one for each vertex in G. The node structure for the lists must contain at least vertex and
link fields. For any given list, z, the nodes in the list contain the vertices that are adjacent
from vertex i. Figure 6.8 shows the adjacency lists for G i, G^, and G4. Notice that each
list has a head node, and that the lists are numbered sequentially. This allows us to
quickly access the adjacency list for any vertex.
The C declarations for the adjacency list representation are:

#define MAX—VERTICES 50 *
/
maximum /
*
number of vertices
typedef struct node *node—pointer;
typedef struct node {
int vertex;
struct node *link;
};
node—pointer graph[MAX—VERTICES];
int n = 0; / * vertices currently in use
/
* */

In the case of an undirected graph with n vertices and e edges, this representation
requires n head nodes and 2e list nodes. Each list node has two fields. Often, you can
sequentially pack the nodes on the adjacency lists, thereby eliminating the use of
pointers. In this case, an array node [ ] may be used, node [z ] gives the starting point of
the list for vertex z, 0<z <n and node [zi ] is set to n + 2e + 1. The vertices adjacent
from vertex i are stored in node [z ], • • •, node [z + !]-!, 0 < z < n. Figure 6.9 gives
such a sequential representation for the graph G4 of Figure 6.5.
We can determine the degree of any vertex in an undirected graph by simply
counting the number of nodes in its adjacency list. This also gives us the number of
edges incident on the vertex. This means that if there are n vertices in the graph G, we
can determine the total number of edges in G in O(n + e) time. For a digraph, we can
determine the out-degree of any vertex by counting the number of nodes in its adjacency
list. This means that we also can determine the total number of edges in a digraph in O(zz
+ e) time. Unfortunately, finding the in-degree of a vertex in a digraph is more complex.
We handle this problem and the related problem of finding all vertices adjacent to a ver­
tex by maintaining a second set of lists. These lists are called inverse adjacency lists.
As was true of adjacency lists, the inverse adjacency lists contain one list for each ver­
tex. However, each list contains a node for each vertex adjacent to the vertex that the
list represents. Figure 6.10 shows the inverse adjacency list for G3.
266 Graphs

headnodes vertex link

0 2 > 3 NULL

1 > 3 NULL

2 1 > 3 NULL

3 1 > 2 NULL

0 1 NULL

1 0 > 2 NULL

2
NULL

0 1 > 2 NULL

1 0 > 3 NULL

2 0 > 3 NULL

3 1 > 2 NULL

4 5 NULL

S 4 > 6 NULL

6 5 7 NULL

7 6 NULL

Figure 6.8 : Adjacency lists for Gj, G^, and G4

Changing the node structure of the adjacency lists is a second approach to the
problem of finding the in-degree of vertices. Figure 6.11 shows a simplified version of
the node structure used in the sparse matrix representation of Section 4.7. Each node
now has four fields and represents one edge. Figure 6.12 shows the representation of G3
using the structure of Figure 6.11. We assume that the head nodes are stored sequen­
tially.
Before discussing the third representation, we would like to quickly reconsider the
lists displayed in Figure 6.8. For each graph, we arranged the nodes in each of the lists
so that the vertices were in ascending order. This is not necessary, and, in fact, vertices
may appear in any order. Thus, the adjacency lists of Figure 6.13 are just as valid a
The Graph Abstract Data Type 267

[0] 9 [8] 23 [16] 2


[1] 11 [9] 1 [17] 5
[2] 13 [10] 2 [18] 4
[3] 15 [11] 0 [19] 6
[4] 17 [12] 3 [20] 5
[5] 18 [13] 0 [21] 7
[6] 20 [14] 3 [22] 6
[7] 22 [15] 1

Figure 6.9 Sequential representation of graph G4

0 1 NULL

1 0 NULL

2
1 NULL

Figure 6.10 : Inverse adjacency list for G3

tail head column link for head row link for tail

Figure 6.11 : Alternate node structure for adjacency lists

representation of G] as the lists in Figure 6.8(a).

Adjacency Multilists

In the adjacency list representation of an undirected graph, we represent each edge,


(v,', Vj), by two entries. One entry is on the list for v,, and the other is on the list for Vj.
As we shall see, in some situations we need to find easily the second entry for an edge
and mark it as having been examined. Maintaining the lists as multilists, that is, lists in
which nodes are shared among several lists, facilitates this operation. For each edge
there is exactly one node, but this node is on the adjacency list for each of the two

Common questions

Powered by AI

Adjacency matrices use a two-dimensional array to represent edges, leading to an access time complexity of O(1) for checking if an edge exists between two vertices . However, the storage requirement is O(n^2), which is inefficient for sparse graphs where most entries are zero . Adjacency lists, on the other hand, use linked lists for each vertex, leading to more efficient space usage, especially for sparse graphs with time complexity for operations such as edge checks being proportional to the vertex degree, typically resulting in O(n + e) time complexity for operations like determining the total number of edges . This makes adjacency lists preferable for sparse graphs with fewer edges while adjacency matrices are better for dense graphs .

Sequential adjacency lists eliminate the use of pointers, thereby reducing memory overhead and potential fragmentation. Instead of pointers, they use array indices to reference positions within a contiguous array. This approach increases cache efficiency due to the sequential access patterns and reduces the complexity associated with pointer management and dynamic memory allocation . As nodes are packed sequentially, this method simplifies algorithms requiring multiple passes over adjacency data, as seen in the sequential representation in Source 3, Figure 6.9.

In adjacency matrices, the degree of a vertex in an undirected graph can be calculated by summing the values of its row (or column, due to symmetry), giving the count of edges incident to it . This results in a time complexity of O(n) since one has to iterate through the entire row. For directed graphs, the row sum gives the out-degree and the column sum provides the in-degree . Conversely, with adjacency lists, the degree is determined by the number of nodes in the vertex’s list, making it directly proportional to the vertex degree itself, leading to O(1) complexity for accessing this information by traversing one linked list . This difference highlights that adjacency lists often provide a more efficient way of calculating vertex degree, particularly for sparse graphs.

In an adjacency matrix for undirected graphs, the matrix is symmetric because the existence of an edge between any two vertices (vi, vj) implies the existence of an edge between (vj, vi). Thus, if adj-mat[i][j] is 1, then adj-mat[j][i] must also be 1 . This symmetry allows optimization in storing only half of the matrix, either the upper or the lower triangle . Symmetry simplifies certain computations and potentially reduces storage demands when implemented efficiently.

Adjacency lists enhance flexibility in graph traversal by efficiently handling sparse graphs via their linked list structure, as only existing edges are stored directly. This structure allows graph traversal algorithms like DFS or BFS to proceed by directly iterating over adjacent vertices without needing to scan entire rows or columns of a matrix, significantly reducing the number of operations . Moreover, the linked nature allows easy integration of vertex-specific metadata or state, which can facilitate algorithms involving complex pathfinding or dynamic graph scenarios where edges might frequently change . In contrast, adjacency matrices require examining O(n^2) entries in the worst case, even if many represent nonexistent edges .

Adjacency matrices are preferred in scenarios where the graph is dense, meaning it has a large number of edges relative to the number of vertices, as the constant-time O(1) complexity for checking the existence of an edge becomes advantageous . They are also preferable when the graph algorithm in question frequently requires fast edge lookups or requires constant-time access patterns, such as in certain matrix algebra operations on graphs. Additionally, matrices can be more suitable in systems where space is not a constraint, but predictable and uniform access times are crucial, as the data structure offers simplified and symmetric manipulation of connections in undirected graphs .

Using adjacency matrices for sparse graphs is inefficient because of the space complexity associated with storing n^2 entries, where n is the number of vertices. Given that sparse graphs have relatively few edges, the matrix will be predominantly filled with zeros, leading to wasted storage . Additionally, operations such as determining if a graph is connected require examining O(n^2) entries, which becomes burdensome and inefficient for large graphs despite many entries being redundant . Adjacency lists, which only store edges that actually exist, reduce both space and potentially improve time complexity for specific queries to O(n + e), where e is the number of edges .

Inverse adjacency lists maintain a list for each vertex that contains a node for each vertex from which there is an incoming edge to the vertex represented by the list . This allows for the out-degree and in-degree computations of each vertex to be handled efficiently by having separate lists for connections directed to each vertex, simplifying the in-degree calculation without the need to traverse the entire graph .

Adjacency multilists store each edge as a single node that is shared among several lists, representing both vertices it connects. This arrangement facilitates easy access and marking of edges during traversal by having the shared node directly indicating the status of the edge (e.g., examined or unexamined) without redundant entries, thus simplifying management and potentially reducing the time complexity of traversal operations . This efficiency is particularly useful in algorithms that require frequent marking and unmarking of edges or paths.

Maintaining inverse adjacency lists alongside standard adjacency lists can double the required storage as each vertex needs a separate list for incoming and outgoing edges . This increased storage requirement can be inefficient, especially in dense graphs with a large number of edges or in systems with constrained memory resources. Furthermore, maintaining and updating both lists during graph mutations (such as edge insertions or deletions) increases algorithmic complexity and may introduce consistency challenges if not properly synchronized . The added complexity might outweigh the performance gains in scenarios where in-degree queries are infrequent.

You might also like