DSA Lab07
DSA Lab07
DSA Lab07
7.1 Objectives
The learning objectives of this laboratory session are:
a
The outcomes for this session are:
e te oc
• Improved skills in C programming using pointers and dynamic memory allocation.
• A clear understanding of graphs, the operations they support, and some of their use.
• The ability to make implementation decisions concerning graphs.
• The ability to decide when graphs are suitable in solving a problem.
nc u p
ie mp Na
7.2 Brief Theory Reminder
D r
Basic Notions
t.
A directed graph (digraph, for short) G = (V, E) is an ordered pair of elements of two sets: V is a set of vertices (or
Sc Co luj-
ep
An arc (also called an edge) is an ordered pair of vertices (v, w); v is called the tail and w the head of the arc. We say
that arc v → w is from v to w, and that sw is adjacent to v.
A path in a digraph is a sequence of vertices v1 , v2 , ..., vn such that v1 → v2 , v2 → v3 , . . ., vn−1 → vn , are arcs, i.e
(vi , vi+1 ) ∈ E. This path is from vertex v1 to vertex vn and passes through vertices v2 , v3 , . . . , vn−1 , and ends at vertex
C
A labeled directed graph is a digraph where every arc and/or vertex has a label associated with it. The label may be a
name, a cost or an arbitrary value.
A digraph is strongly connected if for any two vertices v and w there is a path from v to w (and, of course, one from w
T.
to v).
A complete graph is a graph in which each pair of graph vertices is connected by an edge. The complete graph with n
graph vertices is denoted Kn and has n2 = n(n−1)/2 (the triangular numbers) undirected edges, where nk is a binomial
coefficient. In older literature, complete graphs are sometimes called universal graphs. Figure 7.1 shows complete graphs
K2 , K4 , K5 , K6 , and K8 .
2 3
2
3 2 4 2
3
3 1 1 5 1
4 1
4 6 8
2 1 4 5 5 6 7
1
7 Graph Representations and Traversals
4 3
5 2 3
5 2
6 1
1
7 10
8 9 7 10
a
e te oc
Representation Methods
Both digraph and undirected graphs are usually represented using adjacency matrices and adjacency lists.
For a digraph G = (V, E) with V = {1, 2, . . . , n}, the adjacency matrix is defined as:
nc u p A[i][j] =
1 if (i, j) ∈ E
ie mp Na
0 if (i, j) ∈
/E
D r
t.
label of arc (i, j) if (i, j) ∈ E
A[i][j] =
Sc Co luj-
ep
The adjacency matrix is symmetric for undirected graphs.
In an adjacency list the list of arcs for adjacent nodes is kept. The whole graph may be represented by a table indexed
by nodes, each entry for a node i in the table containing the address of the list of nodes adjacent to i. That list may be a
C
static or a dynamic list. Figure 7.3 shows a graph and its adjacency matrix representation, and Figure 7.4 shows static and
dynamic adjacency lists representations.
U.
0 1 2 3 4
0 1
0 0 1 1 0 0
1 0 0 1 1 1
T.
4 2 2 0 0 0 1 0
3 0 0 0 0 1
3 4 1 0 0 0 0
Traversals
Breadth-first Search
Breadth first search involves the following actions:
1 0
2 1
−1 2
2 3
0 0 3 4
1 3 4 5
2 7 −1 6
3 9 3 7 1 2 •
4 11 −1 8 0
2 3 4 •
4 9 1
−1 10 2 3 •
0 11 3 4 •
−1 12 4
0 •
a
Figure 7.4: Adjacency list representations for the graph of Figure 7.3(a)
e te oc
2. Dequeue one node to process and enqueue all its unvisited adjacent nodes.
nc u p
3. Repeat step 2 until the queue becomes empty.
ie mp Na
D r
t.
A sketch of the algorithm is shown in Listing 7.1.
Sc Co luj-
ep
enum { UNVISITED, VISITED };
Listing 7.1: A breadth-first search implementation for a graph
enqueue(Q, srcNode);
/* srcNode will be the first node dequeued in the loop below */
while(! empty(Q))
{
int v = dequeue(Q);
for (each node w adjacent to v)
if (mark[w] == UNVISITED)
{
mark[w] = VISITED;
process info for w;
enqueue(Q, w);
}
}
}
Depth-first Search
When searching a graph in depth-first search mode, the source node is marked visited first. After visiting all the nodes
reachable from given source, the traversal ends. If there still are unvisited nodes, another node is chosen as a source an
the steps are repeated. The algorithm sketch is shown in Listing 7.2.
s s
b e b e
a d g a d g
c f c f
Q:b Q : a, c, d, e
(a) Before entering the while-loop. (b) After processing nodes adjacent to b.
s
b e
a d g
c f
a
Q : c, d, e
e te oc
(c) After processing nodes adjacent to a.
s s
b e b e
nc u p a d g a d g
ie mp Na
c f c f
D r
Q : d, e, f Q : f, g
t.
(d) After processing nodes adjacent to c. (e) After processing nodes adjacent to d
Sc Co luj-
ep
and e.
s
b e
C
a d g
c f
Q : empty
U.
push(S, srcNode);
while (!empty(S))
{
int v = top(S);
let w be the next unvisited node on AdjList(v);
if (w exists)
{
mark[w] = VISITED;
process info for w;
push(S, w);
4 } Data Structures and Algorithms. Laboratory Guide.
else
pop(S);
}
7.2 Brief Theory Reminder
Note that this algorithm uses a stack to eliminate recursion. A trace of the relevant steps in depth-first search is shown
in Figure 7.6.
s s
s b e b e
b e
a d a d
a d
c c
c S:b
S : a, b S : c, a, b
(a) Before entering the while-loop. (b) After processing node b. (c) After processing node a.
s s
b e b e
a
a d a d
e te oc
c c
S : d, c, a, b S : e, d, c, a, b
D r
t.
Finding Shortest (Minimum Cost) Paths from a Single Vertex
Sc Co luj-
ep
Let G = (V, E) be a labeled directed graph, where every arc is labeled with a non-negative number called a cost. The
graph may be represented using the labeled adjacency matrix (also called a cost matrix).
Given two vertices, one denoted as the source and the other as the destination, one problem is to find the minimum cost
path from the source to the destination. Dijkstra’s solution to this problem is characterized by the following:
C
• A set S of vertices j ∈ V , for which there is at least one path from the source vertex, say s, to the destination, j, is
maintained. Initially S = {s}.
• At each step, a vertex v whose distance to a vertex w ∈ S is minimal, is added to S.
U.
In order to record the minimal paths from the source s to each other vertex, we will use an array parent, in which
parent[k] holds the vertex preceding k on the shortest path.
T.
• The vector parent holds the vertices which are accessible from the source vertices. It allows for path reconstruction
– from a source to any accessible node.
• For nodes which cannot be reached from a source node, S[i] = 0 and dist[i] = ∞.
A implementation of Dijkstra’s algorithm is presented in Listing 7.3.
a
{
find unselected vertex k with dist[k] minimal;
e te oc
if (minimum found == INFTY) return;
S[k] = 1; /* add k to set S */
for (int j = 1; j <= n; j++)
if (S[j] == 0 && dist[k] + cost[k][j] < dist[j])
nc u p
{
dist[j] = dist[k] + cost[k][j];
parent[j] = k;
ie mp Na
}
}
D r
}
t.
Sc Co luj-
ep
All Pairs Shortest Paths
By repeatedly applying Dijkstra’s algorithm with each node as a source, in turn, we can obtain the minimum paths for all
the pairs of vertices in a graph. Another choice is to use R. W. Floyd’s algorithm. Floyd’s algorithm was developed for
solving the all pairs shortest paths problem.
C
The algorithm maintains the minimal costs in an array, say A. Initially, A is identical to the cost matrix, cost. The minimal
distances computation is accomplished in n iterations, where n is the number of nodes. At iteration k, A[i][j] holds the
minimum distance between nodes i and j using paths which do not contain nodes numbered higher than k, except possibly
U.
for the end nodes, i and j. A is calculated with the following formula:
Because Aik (k) = Aik (k−1) and Akj (k) = Akj (k−1) we may use a single copy of array A. Floyd’s algorithm may be
implemented as shown in Listing 7.4.
Listing 7.4: An implementation of Floyd’s algorithm for all pairs shortest paths problem.
#define NMAX ? /* max no. of nodes */
double cost[NMAX][NMAX];
double A[NMAX][NMAX];
For keeping track of the shortest paths, we can make use of an additional table, p, where p[i, j] will hold the vertex
k which lead to the minimum distance A[i, j]. If p[i, j] = 0, then the edge (i, j) is the shortest from i to j. In order to
display the intermediate vertices along the path from i to j, we can use the function in Listing 7.5.
a
Mandatory Assignments
e te oc
Implement modular C programs which have input and output stored in files with names given as command line arguments
to solve the following exercises:
nc u p
7.3.1. Let G be a digraph, i.e. G = (V, E) and let V 0 be one of its subgraphs. If the nodes are represented by integers
which are to be read from the standard input, show the induced subgraph G0 = (V 0 , E 0 ). Input is given as follows:
ie mp Na
V nodes 1 3 5 6 7
D r
V arcs (1 3) (1 5) (1 7) (3 6) (3 7) ...
t.
V’ nodes 1 3 5
Sc Co luj-
ep
Output should look like this:
V’ arcs (1 3) (1 5)
C
Hint. Use fgets and sscanf to read input. E.g. reading a pair of arcs can be accomplished with
sscanf(&buffer[i], "(%d%d)"’, &v, &w) where v and w are integers.
7.3.2. Implement breadth-first and depth-first traversals for a graph represented by an adjacency matrix. Use the algorithms
U.
of §7.2. Input should be as in the lines V nodes and V arcs of exercise 7.3.1.
7.3.3. Implement Dijkstra’s algorithm for minimal cost paths in a directed graph. .
I/O description. Input consists of pairs of nodes given as numbers, separated by a comma and followed by an equal
sign followed by the cost attached to that edge n1 , n2 = n3 which are connected by an edge. E.g.
T.
1,2=22
1,3=11
1,5=5
1,7=5
2,3=55
2,4=41
2,7=33
3,4=8
3,5=4
3,6=9
4,6=17
4,7=9
5,7=20
99 1-7-5-3-6-4-2 (5,20,4,9,17,41)
where the first number is the minimum cost, followed by the list node identifications and then by the list of arc
lengths.
Optional Assignments
7.3.4. Implement graph construction for a graph G = (V, E) for the three representation methods presented at §7.2. Also
write a function to print the graphs. Format for input and output should be the same as in exercise 7.3.1.
7.3.5. Implement a function which should check if there is a path between two given nodes, say v and w, of a directed
graph G = (V, E). Graphs will be read from files as in exercise 7.3.1, and paths will be printed as a sequence of
nodes (i.e integer numbers) separated by a single space.
7.3.6. For an undirected graph G = (V, E), implement a function to check whether or not the graph is strongly connected.
Graphs will be read from files input as in exercise 7.3.1, output ∈ {”yes”, ”no”}.
7.3.7. Implement Floyd’s algorithm. Use the same format for input and output as in the exercise 7.3.3. What can you tell
about the running time of this algorithm?
a
e te oc
nc u p
ie mp Na
D r
t.
Sc Co luj-
ep
C
U.
T.