Theory
Theory
• Graphs are used to represent networks. The networks may include paths in a
city or telephone network or circuit network. For example Google GPS
• Graphs are also used in social networks like linkedIn, Facebook. For example,
in Facebook, each person is represented with a vertex(or node). Each node is
a structure and contains information like person id, name, gender and locale.
• Directed Graphs: The Directed graphs are such graphs in which edges are
directed in a single direction.
For Example, the below graph is a directed graph:
• Undirected Graphs: Undirected graphs are such graphs in which the edges
are directionless or in other words bi-directional. That is, if there is an edge
between vertices u and v then it means we can use the edge to go from
both u to v and v to u.
Representing Graphs
Following two are the most commonly used representations of a graph:
1. Adjacency Matrix.
2. Adjacency List.
The adjacency matrix for the above example undirected graph is:
• Adjacency List: Graph can also be implemented using an array of lists. That
is every index of the array will contain a complete list. Size of the array is
equal to the number of vertices and every index i in the array will store the list
of vertices connected to the vertex numbered i. Let the array be array[]. An
entry array[i] represents the list of vertices adjacent to the ith vertex. This
representation can also be used to represent a weighted graph. The weights
of edges can be represented as lists of pairs. Following is the adjacency list
representation of the above example undirected graph.
C++
// Driver code
int main()
{
int V = 5;
vector<int> adj[V];
addEdge(adj, 0, 1);
addEdge(adj, 0, 4);
addEdge(adj, 1, 2);
addEdge(adj, 1, 3);
addEdge(adj, 1, 4);
addEdge(adj, 2, 3);
addEdge(adj, 3, 4);
printGraph(adj, V);
return 0;
}
Run
Java
import java.util.*;
class Graph {
// Driver Code
public static void main(String[] args)
{
// Creating a graph with 5 vertices
int V = 5;
ArrayList<ArrayList<Integer> > adj
= new ArrayList<ArrayList<Integer> >(V);
printGraph(adj);
}
}
Run
Output:
Pros: Saves space O(|V|+|E|). In the worst case, there can be C(V, 2) number
of edges in a graph thus consuming O(V^2) space. Adding a vertex is easier.
Cons: Queries like whether there is an edge from vertex u to vertex v are not
efficient and can be done O(V).
The BFS traversal of Graphs also traverses the graph in levels. It starts the traversal
with a given vertex, visits all of the vertices adjacent to the initially given vertex and
pushes them all to a queue in order of visiting. Then it pops an element from the
front of the queue, visits all of its neighbours and pushes the neighbours which are
not already visited into the queue and repeats the process until the queue is empty
or all of the vertices are visited.
The BFS traversal uses an auxiliary boolean array say visited[] which keeps track of
the visited vertices. That is if visited[i] = true then it means that the i-th vertex is
already visited.
Complete Algorithm:
1. Create a boolean array say visited[] of size V+1 where V is the number of
vertices in the graph.
2. Create a Queue, mark the source vertex visited as visited[s] = true and push
it into the queue.
3. Until the Queue is non-empty, repeat the below steps:
o Pop an element from the queue and print the popped element.
o Traverse all of the vertices adjacent to the vertex poped from the
queue.
o If any of the adjacent vertex is not already visited, mark it visited and
push it to the queue.
Illustration:
Consider the graph shown in the below Image. The vertices marked blue are not-
visited vertices and the vertices marked yellow are visited. The vertex numbered 1 is
the source vertex, i.e. the BFS traversal will start from the vertex 1.
• Mark the vertex 1 visited in the visited[] array and push it to the queue.
Step 3: POP the vertex at the front of queue that is 1, and print it.
Step 4: Check if adjacent vertices of the vertex 1 are not already visited. If not, mark
them visited and push them back to the queue.
Step 5:
Step 6:
• Check if the adjacent vertices of 4 are not already visited. If not, mark them
visited and push them to queue. So, push 6 to the queue.
Step 9:
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
while (!q.empty()) {
// Pop element at front and print
int node = q.front();
q.pop();
// Driver code
int main()
{
int V = 6;
vector<int> adj[V + 1];
addEdge(adj, 1, 2);
addEdge(adj, 1, 3);
addEdge(adj, 2, 4);
addEdge(adj, 2, 5);
addEdge(adj, 3, 5);
addEdge(adj, 4, 5);
addEdge(adj, 4, 6);
addEdge(adj, 5, 6);
BFS(adj, V);
return 0;
}
Run
Java
import java.util.*;
class Graph {
while (queue.size() != 0)
{
// Dequeue a vertex from queue and print it
s = queue.poll();
System.out.print(s+" ");
// Add it to queue
queue.add(newNode);
}
}
}
}
// Driver Code
public static void main(String[] args)
{
// Creating a graph with 6 vertices
int V = 6;
ArrayList<ArrayList<Integer> > adj
= new ArrayList<ArrayList<Integer> >(V+1);
BFS(adj, V);
}
}
Run
Output:
1 2 3 4 5 6
Depth First Traversal of a Graph
The Depth-First Traversal or the DFS traversal of a Graph is used to traverse a
graph depth wise. That is, it in this traversal method, we start traversing the graph
from a node and keep on going in the same direction as far as possible. When no
nodes are left to be traversed along the current path, backtrack to find a new
possible path and repeat this process until all of the nodes are visited.
We can implement the DFS traversal algorithm using a recursive approach. While
performing the DFS traversal the graph may contain a cycle and the same node can
be visited again, so in order to avoid this we can keep track of visited array using an
auxiliary array. On each step of the recursion mark, the current vertex visited and call
the recursive function again for all the adjacent vertices.
Illustration:
Step 1: Consider the below graph and apply the DFS algorithm recursively for every
node using an auxiliary stack for recursive calls and an array to keep track of visited
vertices.
Step 2: Process the vertex A, mark it visited and call DFS for its adjacent vertices.
Suppose the first adjacent vertex processed is B. The vertex A is put on the auxilary
stack for now.
Step 3: Process the vertex B, mark it visited and call DFS for its adjacent vertices.
Suppose the first adjacent vertex processed is D. The vertex B is put on the auxilary
stack for now.
Step 4: Process the vertex D, mark it visited and call DFS for its adjacent vertices.
Suppose the first adjacent vertex processed is E. The vertex D is put on the auxilary
stack for now.
Step 5: Process the vertex E, mark it visited and call DFS for its adjacent vertices.
Suppose the first adjacent vertex processed is F. The vertex E is put on the auxilary
stack
for now.
Step 6: Process the vertex F, mark it visited and call DFS for its adjacent vertices.
There are no adjacent vertex of the vertex F, so backtrack to find a new path. The
vertex F is put on the auxilary stack for now.
Step 7: Since the vertex F has no adjacent vertices left unvisited, backtrack and go
to previous call, that is process any other adjacent vertex of node E, that is C.
Step 8: Process the vertex C, mark it visited and call DFS for its adjacent vertices.
The vertex C is put on the auxilary stack for now.
Step 9: Since there are no adjacent vertex of C, backtrack to find a new path and
keep removing nodes from stack until a new path is found. Since all of the nodes are
processed so the stack will get empty.
Implementation:
C++
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
int main()
{
// Create a graph given in the above diagram
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
return 0;
}
Run
Java
// Java program to print DFS traversal from a given given graph
import java.io.*;
import java.util.*;
// Constructor
Graph(int v)
{
V = v;
adj = new LinkedList[v];
for (int i=0; i<v; ++i)
adj[i] = new LinkedList();
}
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
g.DFS(2);
}
}
Run
Output:
Following is Depth First Traversal (starting from vertex 2)
2 0 1 3
Detecting Cycle in a Graph
Problem: Given a graph(directed or undirected), check whether the graph contains a
cycle or not.
Solution: Depth First Traversal can be used to detect a cycle in a Graph. DFS for a
connected graph produces a tree. There is a cycle in a graph only if there is a back
edge present in the graph. A back edge is an edge that is from a node to itself (self-
loop) or one of its ancestor in the tree produced by DFS. In the following graph, there
are 3 back edges, marked with a cross sign. We can observe that these 3 back
edges indicate 3 cycles present in the graph.
To detect a back edge, we can keep track of vertices currently in recursion stack of
function for DFS traversal. If we reach a vertex that is already in the recursion stack,
then there is a cycle in the tree. The edge that connects current vertex to the vertex
in the recursion stack is a back edge. We can use an auxiliary array
say, recStack[] to keep track of vertices in the recursion stack.
Therefore, for every visited vertex ‘v’, if there is an adjacent ‘u’ such that u is already
visited and u is not a parent of v, then there is a cycle in the graph. If we don’t find
such an adjacent for any vertex, we say that there is no cycle.
class Graph
{
int V; // No. of vertices
list<int> *adj; // Pointer to an array containing adjacency lists
bool isCyclicUtil(int v, bool visited[], bool *rs); // used by isCyclic()
public:
Graph(int V); // Constructor
void addEdge(int v, int w); // to add an edge to graph
bool isCyclic(); // returns true if there is a cycle in this graph
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
return false;
}
// Driver Code
int main()
{
// Create a graph given in the above diagram
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
if(g.isCyclic())
cout << "Graph contains cycle";
else
cout << "Graph doesn't contain cycle";
return 0;
}
Run
Java
class Graph {
public Graph(int V)
{
this.V = V;
adj = new ArrayList<>(V);
for (int i = 0; i < V; i++)
adj.add(new LinkedList<>());
}
if (visited[i])
return false;
visited[i] = true;
recStack[i] = true;
List<Integer> children = adj.get(i);
recStack[i] = false;
return false;
}
return false;
}
// Driver code
public static void main(String[] args)
{
Graph graph = new Graph(4);
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(1, 2);
graph.addEdge(2, 0);
graph.addEdge(2, 3);
graph.addEdge(3, 3);
if(graph.isCyclic())
System.out.println("Graph contains cycle");
else
System.out.println("Graph doesn't "
+ "contain cycle");
}
}
Run
Output:
The Time Complexity of this method is same as the time complexity of DFS
traversal which is O(V+E), where V is the number of vertices and E is the number of
edges.
Dijkstra's Algorithm for Shortest Path in a Weighted Graph
Given a graph and a source vertex in the graph, find the shortest paths from
source to all vertices in the given graph.
Algorithm:
1. Create a set sptSet (shortest path tree set) that keeps track of vertices
included in shortest path tree, i.e., whose minimum distance from source is
calculated and finalized. Initially, this set is empty.
2. Assign a distance value to all vertices in the input graph. Initialize all distance
values as INFINITE. Assign distance value as 0 for the source vertex so that it
is picked first.
3. While sptSet doesn't include all vertices:
o Pick a vertex u which is not there in sptSet and has minimum distance
value.
o Include u to sptSet.
o Update distance value of all adjacent vertices of u. To update the
distance values, iterate through all adjacent vertices. For every
adjacent vertex v, if sum of distance value of u (from source) and
weight of edge u-v, is less than the distance value of v, then update the
distance value of v.
Let us understand the above algorithm with the help of an example. Consider the
below given graph:
The set sptSet is initially empty and distances assigned to vertices are {0, INF, INF,
INF, INF, INF, INF, INF} where INF indicates infinite. Now pick the vertex with
minimum distance value. The vertex 0 is picked, include it in sptSet.
So sptSet becomes {0}. After including 0 to sptSet, update distance values of its
adjacent vertices. Adjacent vertices of 0 are 1 and 7. The distance values of 1 and 7
are updated as 4 and 8. Following subgraph shows vertices and their distance
values, only the vertices with finite distance values are shown. The vertices included
in SPT are shown in green colour.
Pick the vertex with minimum distance value and not already included in SPT (not in
sptSET). The vertex 1 is picked and added to sptSet. So sptSet now becomes {0, 1}.
Update the distance values of adjacent vertices of 1. The distance value of vertex 2
becomes 12.
Pick the vertex with minimum distance value and not already included in SPT (not in
sptSET). Vertex 7 is picked. So sptSet now becomes {0, 1, 7}. Update the distance
values of adjacent vertices of 7. The distance value of vertex 6 and 8 becomes finite
(15 and 9 respectively).
Pick the vertex with minimum distance value and not already included in SPT (not in
sptSET). Vertex 6 is picked. So sptSet now becomes {0, 1, 7, 6}. Update the
distance values of adjacent vertices of 6. The distance value of vertex 5 and 8 are
updated.
We repeat the above steps until sptSet doesn't include all vertices of given graph.
Finally, we get the following Shortest Path Tree (SPT).
Implementation:
Since at every step we need to find the vertex with minimum distance from the
source vertex from the set of vertices currently not added to the SPT, so we can use
a min heap for easier and efficient implementation. Below is the complete algorithm
using priority_queue(min heap) to implement Dijkstra's Algorithm:
C++
#include<bits/stdc++.h>
using namespace std;
// To add an edge
void addEdge(vector <pair<int, int> > adj[], int u,
int v, int wt)
{
adj[u].push_back(make_pair(v, wt));
adj[v].push_back(make_pair(u, wt));
}
// Prints distance of shortest paths from the source
// vertex to all other vertices
void shortestPath(vector<pair<int,int> > adj[], int V, int src)
{
// Create a priority queue to store vertices that
// are being preprocessed. This is weird syntax in C++.
// Refer below link for details of this syntax
// http://geeksquiz.com/implement-min-heap-using-stl/
priority_queue< iPair, vector <iPair> , greater<iPair> > pq;
// Driver Code
int main()
{
int V = 9;
vector<iPair > adj[V];
shortestPath(adj, V, 0);
return 0;
}
Run
Output:
0 0
1 4
2 12
3 19
4 21
5 11
6 9
7 8
8 14
Note: The Dijkstra's Algorithm doesn't work in the case when the Graph has
negative edge weight.
Bellman-Ford Algorithm for Shortest Path
Problem: Given a graph and a source vertex src in graph, find shortest paths
from src to all vertices in the given graph. The graph may contain negative weight
edges.
1. This step initializes distances from source to all vertices as infinite and
distance to source itself as 0. Create an array dist[] of size |V| with all values
as infinite except dist[src] where src is source vertex.
2. This step calculates shortest distances. Do following |V|-1 times where |V| is
the number of vertices in given graph.
Do following for each edge u-v:
o If dist[v] > dist[u] + weight of edge uv, then update dist[v] as: dist[v] =
dist[u] + weight of edge uv.
3. This step reports if there is a negative weight cycle in graph. Do following for
each edge u-v. If dist[v] > dist[u] + weight of edge uv, then "Graph contains
negative weight cycle".
The idea of step 3 is, step 2 guarantees the shortest distances if the graph doesn't
contain a negative weight cycle. If we iterate through all edges one more time and
get a shorter path for any vertex, then there is a negative weight cycle.
How does this work? Like other Dynamic Programming Problems, the algorithm
calculates shortest paths in a bottom-up manner. It first calculates the shortest
distances which have at most one edge in the path. Then, it calculates the shortest
paths with at-most 2 edges, and so on. After the i-th iteration of the outer loop, the
shortest paths with at most i edges are calculated. There can be maximum |V| -
1 edge in any simple path, that is why the outer loop runs |v| - 1 time. The idea is,
assuming that there is no negative weight cycle, if we have calculated shortest paths
with at most i edges, then an iteration over all edges guarantees to give shortest
path with at-most (i+1) edge.
Example:
Let us understand the algorithm with following example graph. The images are taken
from this source.
Let the given source vertex be 0. Initialize all distances as infinite, except the
distance to the source itself. Total number of vertices in the graph is 5, so all edges
must be processed 4 times.
Let all edges are processed in following order: (B,E), (D,B), (B,D), (A,B), (A,C),
(D,C), (B,C), (E,D). We get following distances when all edges are processed first
time. The first row in shows initial distances. The second row shows distances when
edges (B,E), (D,B), (B,D) and (A,B) are processed. The third row shows distances
when (A,C) is processed. The fourth row shows when (D,C), (B,C) and (E,D) are
processed.
The first iteration guarantees to give all shortest paths which are at most 1 edge
long. We get the following distances when all edges are processed a second time
(The last row shows final values).
The second iteration guarantees to give all shortest paths which are at most 2 edges
long. The algorithm processes all edges 2 more times. The distances are minimized
after the second iteration, so third and fourth iterations don't update the distances.
Implementation:
C++
printArr(dist, V);
return;
}
BellmanFord(graph, 0);
return 0;
}
Run
Java
int V, E;
Edge edge[];
graph.BellmanFord(graph, 0);
}
}
Run
Output:
Important Notes:
Finding the connected components for an undirected graph is an easier task. The
idea is to traverse all of the unvisited vertices, and for each unvisited vertex print, it's
DFS or BFS traversal.
Below is the algorithm following the DFS traversal to find all connected components
in an undirected graph:
Implementation:
C++
public:
Graph(int V); // Constructor
void addEdge(int v, int w);
void connectedComponents();
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
return 0;
}
Run
Java
Output:
Following are connected components
0 1 2
3 4
Sample Problems on Graphs
{1, 1, 0, 0, 0},
{0, 1, 0, 0, 1},
{1, 0, 0, 1, 1},
{0, 0, 0, 0, 0},
{1, 0, 1, 0, 1}
Pseudo Code
// A utility function to do DFS for a
// 2D boolean matrix. It only considers
// the 8 neighbours as adjacent vertices
void DFS(int M[][COL], int row, int col,
bool visited[][COL])
{
// These arrays are used to get
// row and column numbers of 8
// neighbours of a given cell
rowNbr[] = { -1, -1, -1, 0, 0, 1, 1, 1 }
colNbr[] = { -1, 0, 1, -1, 1, -1, 0, 1 }
return count
}
To detect a back edge, we can keep track of vertices currently in recursion stack of
function for DFS traversal. If we reach a vertex that is already in the recursion stack,
then there is a cycle in the tree. The edge that connects current vertex to the vertex
in the recursion stack is a back edge. We have used recStack[] array to keep track of
vertices in the recursion stack.
Pseudo Code
// Utility function to check back edge in a directed graph
bool isCyclicUtil(int v, bool visited[], bool *recStack)
{
if(visited[v] == false)
{
// Mark the current node as visited and part of recursion stack
visited[v] = true
recStack[v] = true
}
recStack[v] = false; // remove the vertex from recursion stack
return false;
}
return false
}
Examples
Solution - The idea is to user Breadth First Search. Below is the algorithm.
1) Create an empty Q.
2) Find all rotten oranges and enqueue them to Q. Also enqueue a delimiter to
indicate the beginning of next time frame.
3) While Q is not empty do following
....3.a) Do following while delimiter in Q is not reached
........ (i) Dequeue an orange from queue, rot all adjacent oranges. While
rotting the adjacent, make sure that the time frame is incremented only once.
And the time frame is not incremented if there are no adjacent oranges.
....3.b) Dequeue the old delimiter and enqueue a new delimiter. The oranges
rotten in the previous time frame lie between the two delimiters.
Pseudo Code
// This function finds if it is possible to rot all oranges or not.
// If possible, then it returns minimum time required to rot all,
// otherwise returns -1
int rotOranges(int arr[][C])
{
// Create a queue of cells
queue < cell > Q
cell temp
ans = 0
// Store all the cells having rotten orange in first time frame
for (int i=0; i < R; i++)
{
for (int j=0; j < C; j++)
{
if (arr[i][j] == 2)
{
temp.x = i
temp.y = j
Q.push(temp)
}
}
}
// Separate these rotten oranges from the oranges which will rotten
// due the oranges in first time frame using delimiter which is (-1, -1
)
temp.x = -1
temp.y = -1
Q.push(temp)
// Process the grid while there are rotten oranges in the Queue
while (!Q.empty())
{
// This flag is used to determine whether even a single fresh
// orange gets rotten due to rotten oranges in current time
// frame so we can increase the count of the required time.
bool flag = false
// Process all the rotten oranges in current time frame.
while (!isdelim(Q.front()))
{
temp = Q.front()
// Check right adjacent cell that if it can be rotten
if (isvalid(temp.x+1, temp.y) && arr[temp.x+1][temp.y] == 1)
{
// if this is the first orange to get rotten, increase
// count and set the flag.
if (!flag) ans++, flag = true