0% found this document useful (0 votes)
32 views14 pages

Unit 3

ads

Uploaded by

shanmuga priya
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)
32 views14 pages

Unit 3

ads

Uploaded by

shanmuga priya
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
You are on page 1/ 14

UNIT III – GRAPHS

Elementary Graph Algorithms: Representations of Graphs – Breadth-First Search – Depth-First


Search – Topological Sort – Strongly Connected Components- Minimum Spanning Trees:
Growing a Minimum Spanning Tree – Kruskal and Prim- Single-Source Shortest Paths: The
Bellman-Ford algorithm – Single-Source Shortest paths in Directed Acyclic Graphs –
Dijkstra„s Algorithm; Dynamic Programming - All-Pairs Shortest Paths: Shortest Paths and
Matrix Multiplication – The Floyd-Warshall Algorithm
Elementary Graph Algorithms:
This method for representing a graph and for searching a graph. Searching a graph means
systematically following the edges of the graph so as to visit the vertices of the graph. A
graph-searching algorithm can discover much about the structure of a graph. Many
algorithms begin by searching their input graph to obtain this structural information. Other
graph algorithms are organized as simple elaborations of basic graph-searching algorithms.
Techniques for searching a graph are at the heart of the field of graph algorithms.
Representations of graphs
There are two standard ways to represent a graph G = (V, E): as a collection of adjacency
lists or as an adjacency matrix.
The adjacency-list representation of a graph G = (V, E) consists of an array Adj of |V| lists,
one for each vertex in V. For each u V, the adjacency list Adj[u] contains (pointers to) all
the vertices v such that there is an edge (u,v) E. That is, Adj[u] consists of all the vertices
adjacent to u in G.

Figure 23.1 Two representations of an undirected graph. (a) An undirected graph


G having five vertices and seven edges. (b) An adjacency-list representation of G.
(c) The adjacency-matrix representation of G.
Figure 23.2 Two representations of a directed graph. (a) A directed graph G
having six vertices and eight edges. (b) An adjacency-list representation of G. (c)
The adjacency-matrix representation of G.

Adjacency lists can readily be adapted to represent weighted graphs, that is, graphs for which
each edge has an associated weight, typically given by a weight function w : E R. For
example, let G = (V, E) be a weighted graph with weight function w. The weight w(u,v) of the
edge (u,v) E is simply stored with vertex v in u's adjacency list. The adjacency-list
representation is quite robust in that it can be modified to support many other graph variants.

A potential disadvantage of the adjacency-list representation is that there is no quicker way to


determine if a given edge (u,v) is present in the graph than to search for v in the adjacency
list Adj[u]. This disadvantage can be remedied by an adjacency-matrix re presentation of the
graph, at the cost of using asymptotically more memory.

For the adjacency-matrix representation of a graph G = (V, E), we assume that the vertices are
numbered 1, 2, . . . , |V| in some arbitrary manner. The adjacency-matrix representation of a
graph G then consists of a |V| |V| matrix A = (aij) such that

Breadth-first search
Breadth-first search is one of the simplest algorithms for searching a graph and the archetype
for many important graph algorithms. Dijkstra's single-source shortest-paths algorithm
(Chapter 25) and Prim's minimum-spanning-tree algorithm (Section 24.2) use ideas similar
to those in breadth-first search.
BFS(G,s)
1 for each vertex u V[G] - {s}
2 do color[u] WHITE
3 d[u]
4 [u] NIL
5 color[s] GRAY
6 d[s] 0
7 [s] NIL
8 Q {s}
9 while Q
10 do u head[Q]
11 for each v Adj[u]
12 do if color[v] = WHITE
13 then color[v] GRAY
14 d[v] d[u] + 1
15 [v] u
16 ENQUEUE(Q,v)
17 DEQUEUE(Q)
18 color[u] BLACK

Figure 23.3 illustrates the progress of BFS on a sample graph.

The procedure BFS works as follows. Lines 1-4 paint every vertex white, set d [u]
to be infinity for every vertex u, and set the parent of every vertex to be NIL. Line 5
paints the source vertex s gray, since it is considered to be discovered when the
procedure begins. Line 6 initializes d[s] to 0, and line 7 sets the predecessor of the
source to be NIL. Line 8 initializes Q to the queue containing just the vertex s;
thereafter, Q always contains the set of gray vertices.

The main loop of the program is contained in lines 9-18. The loop iterates as long
as there remain gray vertices, which are discovered vertices that have not yet had
their adjacency lists fully examined. Line 10 determines the gray vertex u at the
head of the queue Q. The for loop of lines 11-16 considers each vertex v in the
adjacency list of u. If v is white, then it has not yet been discovered, and the
algorithm discovers it by executing lines 13-16. It is first grayed, and its
distance d[v] is set to d[u] + 1. Then, u is recorded as its parent. Finally, it is placed
at the tail of the queue Q. When all the vertices on u's adjacency list have been
examined, u is removed from Q and blackened in lines 17-18.

Figure 23.3 The operation of BFS on an undirected graph.

Analysis

Before proving all the various properties of breadth-first search, we take on the somewhat
easier job of analyzing its running time on an input graph G = (V,E). After initialization, no
vertex is ever whitened, and thus the test in line 12 ensures that each vertex is enqueued at most
once, and hence dequeued at most once. The operations of enqueuing and dequeuing take O(1)
time, so the total time devoted to queue operations is O(V). Because the adjacency list of each
vertex is scanned only when the vertex is dequeued, the adjacency list of each vertex is scanned
at most once. Since the sum of the lengths of all the adjacency lists is (E), at most O(E) time
is spent in total scanning adjacency lists. The overhead for initialization is O(V), and thus the
total running time of BFS is O(V + E). Thus, breadth-first search runs in time linear in the size
of the adjacency- list representation of G.

Depth-first search
As in breadth-first search, whenever a vertex v is discovered during a scan of the adjacency list
of an already discovered vertex u, depth-first search records this event by setting v's
predecessor field [v] to u. Unlike breadth-first search, whose predecessor subgraph forms a
tree, the predecessor subgraph produced by a depth-first search may be composed of several
trees, because the search may be repeated from multiple sources. The predecessor subgraph of
a depth-first search is therefore defined slightly differently from that of a breadth-first search.

DFS(G)
1 for each vertex u V[G]
2 do color[u] WHITE
3 [u] NIL
4 time 0
5 for each vertex u V[G]
6 do if color[u] = WHITE
7 then DFS-VISIT(u)

Figure 23.4 illustrates the progress of DFS on the graph shown in Figure 23.2.

Procedure DFS works as follows. Lines 1-3 paint all vertices white and initialize their fields
to NIL. Line 4 resets the global time counter. Lines 5-7 check each vertex in V in turn and,
when a white vertex is found, visit it using DFS-VISIT. Every time DFS-VISIT(u) is called in
line 7, vertex u becomes the root of a new tree in the depth-first forest. When DFS returns,
every vertex u has been assigned a discovery time d[u] and a finishing time â[u].

In each call DFS-VISIT(u), vertex u is initially white. Line 1 paints u gray, and line 2 records
the discovery time d[u] by incrementing and saving the global variable time. Lines 3-6 examine
each vertex v adjacent to u and recursively visit v if it is white. As each vertex v Adj[u] is
considered in line 3, we say that edge (u, v) is explored by the depth-first search. Finally, after
every edge leaving u has been explored, lines 7-8 paint u black and record the finishing time
in â[u].

Figure 23.4 The progress of the depth-first-search algorithm DFS on a directed


graph.

Topological sort
A topological sort of a dag G = (V, E) is a linear ordering of all its vertices such
that if G contains an edge (u, v), then u appears before v in the ordering. (If
the graph is not acyclic, then no linear ordering is possible.) A topological sort
of a graph can be viewed as an ordering of its vertices along a horizontal line
so that all directed edges go from left to right. Topological sorting is thus
different from the usual kind of "sorting".

The following simple algorithm topologically sorts a dag.


TOPOLOGICAL-SORT(G)
1 call DFS(G) to compute finishing times f[v] for each vertex v
2 as each vertex is finished, insert it onto the front of a linked list
3 return the linked list of vertices

A directed graph G is acyclic if and only if a depth-first search of G yields no back


edges.
Proof : Suppose that there is a back edge (u, v). Then, vertex v is an ancestor of
vertex u in the depth-first forest. There is thus a path from v to u in G, and the back
edge (u, v) completes a cycle.

Figure 23.8 A dag for topological sorting.

Strongly connected components


Strongly connected component of a directed graph G = (V, E) is a maximal set of
vertices U V such that for every pair of vertices u and v in U, we have
both that is, vertices u and v are reachable from each other.
Our algorithm for finding strongly connected components of a graph G = (V, E) uses the
transpose of G, which is defined in Exercise 23.1-3 to be the graph GT = (V, ET), where ET =
{(u, v): (v, u) E}. That is, ET consists of the edges of G with their directions reversed. Given
an adjacency-list representation of G, the time to create GT is O(V + E).

STRONGLY-CONNECTED-COMPONENTS(G)
1 call DFS(G) to compute finishing times f[u] for each vertex u
2 compute GT
3 call DFS(GT), but in the main loop of DFS, consider the vertices
in order of decreasing f[u] (as computed in line 1)
4 output the vertices of each tree in the depth-first forest of step 3 as a
separate strongly connected component

Minimum Spanning Tree:

What is a Minimum Spanning Tree?


The cost of the spanning tree is the sum of the weights of all the edges in the tree. There can be many
spanning trees. Minimum spanning tree is the spanning tree where the cost is minimum among all the
spanning trees. There also can be many minimum spanning trees.
Minimum spanning tree has direct application in the design of networks. It is used in algorithms
approximating the travelling salesman problem, multi-terminal minimum cut problem and minimum-cost
weighted perfect matching. Other practical applications are:
Cluster Analysis
Handwriting recognition
Image segmentation

There are two famous algorithms for finding the Minimum Spanning Tree:

Kruskal’s Algorithm

Kruskal’s Algorithm builds the spanning tree by adding edges one by one into a growing spanning tree.
Kruskal's algorithm follows greedy approach as in each iteration it finds an edge which has least weight and
add it to the growing spanning tree.
Algorithm Steps:
Sort the graph edges with respect to their weights.
Start adding edges to the MST from the edge with the smallest weight until the edge of the largest weight.
Only add edges which doesn't form a cycle , edges which connect only disconnected components.
So now the question is how to check if 2 vertices are connected or not ?
This could be done using DFS which starts from the first vertex, then check if the second vertex is visited or
not. But DFS will make time complexity large as it has an order of �(�+�) where � is the number of
vertices, � is the number of edges. So the best solution is "Disjoint Sets":
Disjoint sets are sets whose intersection is the empty set so it means that they don't have any element in
common.
Consider following example:
In Kruskal’s algorithm, at each iteration we will select the edge with the lowest weight. So, we will start
with the lowest weighted edge first i.e., the edges with weight 1. After that we will select the second lowest
weighted edge i.e., edge with weight 2. Notice these two edges are totally disjoint. Now, the next edge will
be the third lowest weighted edge i.e., edge with weight 3, which connects the two disjoint pieces of the
graph. Now, we are not allowed to pick the edge with weight 4, that will create a cycle and we can’t have
any cycles. So we will select the fifth lowest weighted edge i.e., edge with weight 5. Now the other two
edges will create cycles so we will ignore them. In the end, we end up with a minimum spanning tree with
total cost 11 ( = 1 + 2 + 3 + 5).

Prim’s Algorithm
Prim’s Algorithm also use Greedy approach to find the minimum spanning tree. In Prim’s
Algorithm we grow the spanning tree from a starting position. Unlike an edge in Kruskal's,
we add vertex to the growing spanning tree in Prim's.

Algorithm Steps:

 Maintain two disjoint sets of vertices. One containing vertices that are in the growing
spanning tree and other that are not in the growing spanning tree.
 Select the cheapest vertex that is connected to the growing spanning tree and is not in
the growing spanning tree and add it into the growing spanning tree. This can be done
using Priority Queues. Insert the vertices, that are connected to growing spanning tree,
into the Priority Queue.
 Check for cycles. To do that, mark the nodes which have been already selected and
insert only those nodes in the Priority Queue that are not marked.

Consider the example below:


In Prim’s Algorithm, we will start with an arbitrary node (it doesn’t matter which one) and
mark it. In each iteration we will mark a new vertex that is adjacent to the one that we have
already marked. As a greedy algorithm, Prim’s algorithm will select the cheapest edge and
mark the vertex. So we will simply choose the edge with weight 1. In the next iteration we have
three options, edges with weight 2, 3 and 4. So, we will select the edge with weight 2 and mark
the vertex. Now again we have three options, edges with weight 3, 4 and 5. But we can’t choose
edge with weight 3 as it is creating a cycle. So we will select the edge with weight 4 and we
end up with the minimum spanning tree of total cost 7 ( = 1 + 2 +4).

Time Complexity:
The time complexity of the Prim’s Algorithm is because each edge is inserted in the
priority queue only once and insertion in priority queue take logarithmic time.

Single-Source Shortest Paths:


The problem is also sometimes called the single-pair shortest path problem, to distinguish it from the
following variations:

 The single-source shortest path problem, in which we have to find shortest paths from a source
vertex v to all other vertices in the graph.
 The single-destination shortest path problem, in which we have to find shortest paths from all
vertices in the directed graph to a single destination vertex v. This can be reduced to the single-source
shortest path problem by reversing the arcs in the directed graph.
 The all-pairs shortest path problem, in which we have to find shortest paths between every pair of
vertices v, v' in the graph.

The Bellman-Ford algorithm


The single source shortest path algorithm (for arbitrary weight positive or negative) is also
known Bellman-Ford algorithm is used to find minimum distance from source vertex to any
other vertex. The main difference between this algorithm with Dijkstra’s algorithm is, in
Dijkstra’s algorithm we cannot handle the negative weight, but here we can handle it easily.

Bellman-Ford algorithm finds the distance in bottom up manner. At first it finds those
distances which have only one edge in the path. After that increase the path length to find all
possible solutions.
Input − The cost matrix of the graph:
06∞7∞
∞ 0 5 8 -4
∞ -2 0 ∞ ∞
∞ ∞ -3 0 9
2∞7∞0
Output − Source Vertex: 2Vert: 0 1 2 3 4Dist: -4 -2 0 3 -6Pred: 4 2 -1 0 1The graph has no
negative edge cycle
Algorithm
bellmanFord(dist, pred, source)
Input − Distance list, predecessor list and the source vertex.
Output − True, when a negative cycle is found.
Begin
iCount := 1
maxEdge := n * (n - 1) / 2 //n is number of vertices
for all vertices v of the graph, do
dist[v] := ∞
pred[v] := ϕ
done
dist[source] := 0
eCount := number of edges present in the graph
create edge list named edgeList
while iCount < n, do
for i := 0 to eCount, do
if dist[edgeList[i].v] > dist[edgeList[i].u] + (cost[u,v] for edge i)
dist[edgeList[i].v] > dist[edgeList[i].u] + (cost[u,v] for edge i)
pred[edgeList[i].v] := edgeList[i].u
done
done
iCount := iCount + 1
for all vertices i in the graph, do
if dist[edgeList[i].v] > dist[edgeList[i].u] + (cost[u,v] for edge i), then
return true
done
return false
End
Single-Source Shortest paths in Directed Acyclic Graphs
Dijkstra’s Algorithm – Single Source Shortest Path Algorithm
Dijkstra’s Algorithm is also known as Single Source Shortest Path (SSSP) problem. It is used
to find the shortest path from source node to destination node in graph.

The graph is widely accepted data structure to represent distance map. The distance between
cities effectively represented using graph.
 Dijkstra proposed an efficient way to find the single source shortest path from the
weighted graph. For a given source vertex s, the algorithm finds the shortest path to
every other vertex v in the graph.
 Assumption : Weight of all edges is non-negative.
 Steps of the Dijkstra’s algorithm are explained here:
1. Initializes the distance of source vertex to zero and remaining all other vertices to
infinity.

2. Set source node to current node and put remaining all nodes in the list of unvisited
vertex list. Compute the tentative distance of all immediate neighbour vertex of the current
node.

3. If the newly computed value is smaller than the old value, then update it.

For example, C is the current node, whose distance from source S is dist (S, C) = 5.

 Consider N is the neighbour of C and weight of edge


(C, N) is 3. So the distance of N from source via C would be 8.
 If the distance of N from source was already computed and if it is greater than 8 then
relax edge (S, N) and update it to 8, otherwise don’t update it.

d(S, N) = 11
d(S, N) = 7
d(S, C) + d(C, N) < d(S, N) ⇒ Relax
d(S, C) + d(C, N) > d(S, N) ⇒ Don’t
edge (S, N)
update d(S, N)
Update d(S, N) = 8
Weight updating in Dijkstra’s algorithm
4. When all the neighbours of a current node are explored, mark it as visited. Remove it
from unvisited vertex list. Mark the vertex from unvisited vertex list with minimum distance
and repeat the procedure.

5. Stop when the destination node is tested or when unvisited vertex list becomes empty.
Dynamic programming
Dynamic Programming (DP) is defined as a technique that solves some particular
type of problems in Polynomial Time. Dynamic Programming solutions are faster than
the exponential brute method and can be easily proved their correctness.
Characteristics of Dynamic Programming Algorithm:
 In general, dynamic programming (DP) is one of the most powerful techniques for
solving a certain class of problems.
 There is an elegant way to formulate the approach and a very simple thinking process,
and the coding part is very easy.
 Essentially, it is a simple idea, after solving a problem with a given input, save the
result as a reference for future use, so you won’t have to re-solve it.. briefly ‘Remember
your Past’ :).
 It is a big hint for DP if the given problem can be broken up into smaller sub-problems,
and these smaller subproblems can be divided into still smaller ones, and in this
process, you see some overlapping subproblems.
 Additionally, the optimal solutions to the subproblems contribute to the optimal
solution of the given problem (referred to as the Optimal Substructure Property).
 The solutions to the subproblems are stored in a table or array (memoization) or in a
bottom-up manner (tabulation) to avoid redundant computation.
 The solution to the problem can be constructed from the solutions to the subproblems.
 Dynamic programming can be implemented using a recursive algorithm, where the
solutions to subproblems are found recursively, or using an iterative algorithm, where
the solutions are found by working through the subproblems in a specific order.
Dynamic programming works on following principles:
 Characterize structure of optimal solution, i.e. build a mathematical model of the
solution.
 Recursively define the value of the optimal solution.
 Using bottom-up approach, compute the value of the optimal solution for each possible
subproblems.
 Construct optimal solution for the original problem using information computed in the
previous step.

Applications:
ynamic programming is used to solve optimization problems. It is used to solve many real-
life problems such as,
(i) Make a change problem
(ii) Knapsack problem
(iii) Optimal binary search tree

Floyd–Warshall algorithm
The Floyd–Warshall algorithm compares many possible paths through the graph between each
pair of vertices. It is guaranteed to find all shortest paths and is able to do this

with comparisons in a graph, even though there may be edges in the graph. It does
so by incrementally improving an estimate on the shortest path between two vertices, until the
estimate is optimal.

Example

Algorithm
Step 1 − Construct an adjacency matrix A with all the costs of edges present in the graph. If
there is no path between two vertices, mark the value as ∞.
Step 2 − Derive another adjacency matrix A1 from A keeping the first row and first column of
the original adjacency matrix intact in A1. And for the remaining values, say A1[i,j],
if A[i,j]>A[i,k]+A[k,j] then replace A1[i,j] with A[i,k]+A[k,j]. Otherwise, do not change the
values. Here, in this step, k = 1 (first vertex acting as pivot).
Step 3 − Repeat Step 2 for all the vertices in the graph by changing the k value for every pivot
vertex until the final matrix is achieved.
Step 4 − The final adjacency matrix obtained is the final solution with all the shortest paths.
Analysis
From the pseudocode above, the Floyd-Warshall algorithm operates using three for loops to
find the shortest distance between all pairs of vertices within a graph. Therefore, the time
complexity of the Floyd-Warshall algorithm is O(n3), where ‘n’ is the number of vertices in
the graph. The space complexity of the algorithm is O(n2).

You might also like