You are on page 1of 136

#LifeKoKaroLift

Graphs
Introduction to Graphs

Isn’t this the first image that comes to your mind when you
hear the word graphs???

Do you think the graphs used in programming will be relatable


to the graphs you used in your school life???

Well there’s only one way to find out...Let’s study GRAPHS!!!


Introduction to Graphs

● Here’s a map showing the Duronto train


services in India as of March 2010.

● Which data structure do you think is


best to represent such form of data?

● It is quite clear that using a linear data


structure isn’t the best choice as it will
get very complicated.

● So, do you think trees and heaps can


provide a solution to this???
Introduction to Graphs

● If you have noticed, despite being non-linear data structures, trees and heaps
have some sort of hierarchical flow from parent nodes to children nodes,
which is clearly undesirable in our problem here.

● So, this brings us to our newest and one of the most versatile and used
abstract data structure, GRAPH.

● Graph data structures can represent the relationship between different


objects/entities. Here, the entities are called as nodes(vertices) and the
relationship is called as edges(arcs).
Introduction to Graphs

● We can also use graphs to represent


a social network

● In this example, different people are


represented as nodes and the edges
connecting between them represent
the friendship relation.

● The absence of an edge between A


and C means that they are not friends
Introduction to Graphs

The mathematical definition of graph states that:

G = (V, E) Here G is a graph which is a pair of two sets where:

V - It is a finite set of vertices (nodes).

E - It is a finite set of an ordered pair of the form (u, v) called an edge.


The pair (u, v) indicates that there is an edge from vertex u to vertex v.
Introduction to Graphs

Graphs play a critical role in the application of social networks, transportation


networks, etc. Certain applications of the graph data structure are:

● Graphs help in finding the shortest distance between any two given
locations, where nodes can be the different locations in a city and edges
represent the routes available between the locations.

● Graphs help in identifying contacts on social sites and suggesting them


as friends on Facebook, where the nodes can be different users and the
edges represent the relationships between them.
Introduction to Graphs

● Graph data structures do not follow any restriction like that of the tree data
structure on how different nodes are connected to each other.
● Tree data structures have usage restrictions in representing real-world
scenarios as each child node can have only one parent node.
Introduction to Graphs

● Let’s move back to our example of social networks


represented by graphs.

● Let’s assume that the person ABC is related to XYZ.


Does that mean that XYZ is also related back to the
person ABC???

● Well, if we take the example facebook, this relation


does not have any direction because both of them can
be friends or not.

● But what about Instagram, does all your following follow


you back? Do you follow back all your followers?
Introduction to Graphs

● A graph that shows a symmetrical relationship between two connected


nodes paired by an edge that represents a simple line segment is called an
Undirected Graph.

● The facebook social network is an example of


Undirected Graph because only one of the two
people needs to send the friend request and the
relationship is established wherein both the people
are equal.

● Another example is the graph shown in the right


image.
Introduction to Graphs

● Graphs that show an asymmetrical relationship between two connected


nodes and are paired by an edge that indicates the direction of the
relationship from one node to another is called a Directed graph.

● The instagram social network is an example of


directed graphs. This is because if someone accepts
your follow request, it doesn’t imply that they
become your follower too (unlike facebook). The
relationship is unidirectional and they have to send a
separate follow request for it to be bidirectional.

● The given image shows another example.


Introduction to Graphs

● Furthermore, these graphs (directed or undirected) may or may not have


cycles in them.

● The directed graphs that have cycles in them, i.e. if you start from any node
in the graph and traverse through its connecting edges, you return to the
same node where you started, are called Directed Cyclic Graphs.

● Similarly, the directed graphs that don’t have any cycle in them are called
Directed Acyclic Graphs(DAG).

● The example of a Directed Cyclic Graph can be our instagram social network
where A follows B, B follows C and C follows A. If no such cycle exists, it
would be an example of DAG.
Introduction to Graphs

● This is the same example we saw while learning about Directed Graphs. Is
this an example of Directed Cyclic Graph or Directed Acyclic Graph???
Introduction to Graphs

● Now we move on to a special type of Directed


Acyclic Graph( DAG) and yes, it’s the same
example we have been using.

● There is a special kind of DAG which has the


condition that no node can be related to two
different nodes, like the node C and D in this
example.

● Can you try to guess what would we call such


a DAG? Let’s see another example to help you
visualize it better.
Introduction to Graphs

● The given DAG has a


person H who is related
to both B and C( There
is no such other node).

● What if H was related to


one and only one
person, say B.

● What would you call


such a data structure?
Introduction to Graphs

● Well, you are right this


kind of data structure is
what we have already
studied.

● It’s an example of tree.

● Therefore, we can
safely say that all the
trees are examples of
graphs (Directed
Acyclic Graphs).
Introduction to Graphs
Let’s summarise what we learnt about the type of graphs:
Introduction to Graphs

● There are two types of graphs on an another basis of differentiation:

○ Connected Graph: A Graph is connected if a path exists from every


vertex to every other vertex.

○ Disconnected Graph: A Graph is disconnected if there exist at least


two nodes such that there is no path connecting them.

● Let’s see an example to solidify this theory:


Introduction to Graphs

Let’s analyze the given two graphs carefully now:

● In the first image( the top one), all the nodes seem to
have one or more connection(s) with other nodes and
hence all the nodes are connected to each other, either
directly or indirectly. It is therefore, a connected graph.

● But, in the bottom graph all the connections of node 5


are removed from the the above graph leaving no path
from 5 to any of the node. Thus, it is a disconnected
graph.
Terminology

Let’s now see the various terms related to this data structure:

● Neighbours: If two nodes are adjacent to each other and connected by an


edge, then those nodes are called neighbours.

● Degree: The number of edges that are connected to a node is called the
degree of that node. In directed graphs, the degree can be classified into:

○ In-degree: The number of incoming edges to a node

○ Out-degree: The number of outgoing edges from a node


Terminology

For a directed graph:

Degree = In-Degree(the edges pointing to the vertex)


+
Out-Degree(the edges pointing away from the vertex)

Well it’s time to test how well you understood the


concept of neighbours and degree…

What will be the neighbours and degree of each


of the nodes in the graph??
Terminology

You can verify your answers here...

Nodes Neighbours Degree

Node 1 {2, 3, 4, 5} 4

Node 2 {1, 4} 2

Node 3 {1} 1

Node 4 {1, 2, 5} 3

Node 5 {1, 4} 2
Terminology

Let’s move on to the next term:

● Path:
○ When a series of vertices are
connected by a sequence of edges
between two specific nodes in a
graph, the sequence is called a path.

○ For example, in the given graph, {2, 1,


4, 5} indicates the path between the
nodes 2 and 5, and the intermediate
nodes are 1 and 4.
Terminology

● Weighted graph:

○ A graph in which edges contain


some weights or values is called a
weighted graph.

○ Example: If the nodes in the graph are


considered as the cities and edges
considered as the paths between
them then the weights of these edges
can be considered as the distance
between these cities.
Terminology

● Unweighted graph:

○ A graph in which edges contain no


weight is called the Unweighted
Graph.

○ Example: If the edges from previous


example do not have any weights
assigned to them, the it becomes an
unweighted graph.
Depth First Search

● Remember DFS from binary trees???

● How we used to traverse in a single direction from a root node till end and
then backtrack and change direction...

● Well, DFS in graphs is not so much different either…

● From the start node, it traverses through any one of its neighbours and
explores the farthest possible UNVISITED node in each branch before
backtracking.
Depth First Search

● Backtracking happens when the search algorithm reaches a node where


there are no neighbours to visit, or all the neighbours have already been
visited.

● Then, the DFS algorithm traces back to the previous node and
traverses any neighbour if it is left unvisited.

● Thus, backtracking helps to traverse through all the connected nodes


in the graph and trace back to the start node.

● Let’s look at an example to understand this algorithm better...


Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

Let’s perform DFS on this graph…

● Let’s assume, we start with node 2. We can


choose any of the neighbours(1 & 4) to visit first.

● Let’s say we choose 1. Now, we have 4


neighbours but only three choices, 3,4 and 5
about who to visit next. This is because, the
fourth neighbour, node 1 is already visited and
cannot be visited again as long as there are
other unvisited neighbours to cover all nodes.

● Let’s go with 3. Now, on reaching 3, we observe that it has no neighbour that


has not already been visited.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first


time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

● This is where we backtrack for the first All nodes visited. DFS Complete
time, i.e. visit 1 again. Now, we choose
to visit another neighbour, either 4 or 5.

● Let’s visit 4. Now, 4 has its own three


neighbours, but two of them have
already been visited( 1 and 2).
Therefore, we now visit 5.

● Now, both the neighbours of 5 have


been visited already and hence, we
backtrack to 4.
Depth First Search

The following image summarizes our entire DFS traversal example:

Nodes
2 21 213 2131
Traversed:

21314542 2131454 213145 21314


Depth First Search

Now that you have understood the algorithm, let’s check the pseudocode:

Visited ← { }

procedure dfs(n)
add n to visited set
for all n` ∈ neighbours(n) do
if (n` ∉ visited) then
dfs(n`)
end if
end for
end procedure
Depth First Search

Well, we saw the flow of logic first and then, the pseudocode for our algorithm as
well. Let’s solidify our DFS concepts by dry-running our algorithm on our example:

● The first step


obviously is to call
the DFS function on
our starting node,
i.e. node 2 in our
example.

● Thus, it is the first


element to be added
in the visited set.
Depth First Search

● Now, we visit one of the neighbours of 2( 1 and 4). Let’s proceed with 1 as we
did earlier.

● So, we now call the


DFS function on
node 1.

● The first thing done


on this function call
is addition of 1 to the
visited set.
Depth First Search

● Now, we check all the neighbours of 1(2, 3, 4 and 5). Since 2 is already in the
visited set, we move to the next neighbour which is 3.

● So, we now call the


DFS function on
node 3.

● The first thing done


on this function call
is addition of 3 to the
visited set.
Depth First Search

● Here, 3 has only one neighbour, the node 1 which has already been visited.
So, we backtrack from 3 to our previous call, i.e. DFS 1. Now we move on
the neighbour, after node 3, which is 4.
● So, we now call the
DFS function on
node 4.

● The first thing done


on this function call
is addition of 4 to the
visited set.
Depth First Search

● Here, 4 has three neighbours 1, 2 and 5. But since 1 and 2 are already visited
and 5 is not, we move on with the node 5.

● So, we now call the


DFS function on
node 5.

● The first thing done


on this function call
is addition of 5 to the
visited set.
Depth First Search

● Here, 5 has two neighbours 1 and 4, both of which have been visited. Since,
there is no unvisited neighbour of 5, we perform another backtracking and
check for the unvisited neighbours in our previous DFS call.
● The previous DFS
call, which has no
unvisited neighbours.

● Similarly, on
backtracking to all
the previous calls
including, top-most
DFS 2, all the nodes
are visited and our
DFS is complete.
Depth First Search

As we saw, the algorithm uses a set called Visited to store all the visited
elements during our traversal. Let’s further break down this algorithm into steps:

1. The start node of the DFS algorithm is added to the visited set.

2. The ‘for’ loop instruction set is executed for all the neighbours of the start
node.

3. Then if the neighbour node is unvisited, only then it recursively calls the
dfs() method.

4. Now, the unvisited neighbour node becomes the start node and repeats the
above steps.
Depth First Search

5. The DFS traversal reaches a node from where there are no unvisited
neighbour nodes; here, the recursive algorithm backtracks to the earlier
unvisited, neighbour nodes that are stored in the stack and visits the
remaining nodes.

● Thus, the depth-first search recursively visits all the nodes along one branch
and then backtracks to the unvisited neighbour nodes.

● Once all the nodes connected from the start node are visited, the algorithm
ends.
Depth First Search

● But we have also studied that it is also possible


in a graph for a node to have exactly 0
neighbours.

● Such types of graphs, as we studied are called


Disconnected Graphs.

● As you may have already visualized, there is


no way we can visit the isolated node from
any of the other nodes through our
algorithm.
Depth First Search

Well you have seen the algorithm, let’s dry run it on


the given example:

Step 1: Now run dfs() method on node ‘1’ and add


that node to the visited list.

Step 2: dfs(1) method recursively calls for all the


unvisited neighbours of node ‘1’:

● Here the unvisited neighbours of node ‘1’ are


{2, 3}
● Let us assume that dfs(1) recursively calls for
node ‘2’ first and add the node to the visited list.
Depth First Search

Step 3: Now, dfs(2) method recursively calls for all


the unvisited neighbours of node ‘2’:

● Here the unvisited neighbours of node ‘2’ are


{3, 4}
● Let us assume that dfs(2) recursively calls for
node ‘3’ first and add the node to the visited list.

Step 4: Now, dfs(3) method recursively calls for all


the unvisited neighbours of node ‘3’. Since there are
no remaining unvisited neighbours of the node ‘3’ it
returns back.
Depth First Search

Step 5: Now, dfs(2) method recursively calls for all


the unvisited neighbours of node ‘2’:

● Here the unvisited neighbours of node ‘2’ is {4}


so, dfs(2) recursively calls for node ‘4’ and add
the node to the visited list.

Step 6: Now, dfs(4) method recursively calls for all


the remaining unvisited neighbours of node ‘4’.
Since there are no remaining unvisited neighbours
of the node ‘4’ it returns back.
Depth First Search

Step 7: Now, dfs(2) method recursively calls for all


the remaining unvisited neighbours of node ‘2’.
Since there are no remaining unvisited neighbours
of the node ‘2’ it returns back.

Step 8: Now, dfs(1) method recursively calls for all


the remaining unvisited neighbours of node ‘1’.
Since there are no remaining unvisited neighbours
of the node ‘1’ it returns back.

The visited list is the dfs of the graph...


Depth First Search

The following image summarizes the dry run:


Depth First Search

● Well, there is one more approach to


implement our algorithm.

● We know that there is a relation


between recursion and stack, let us
see the DFS of a graph using stacks
with an example.

● We’ll be using the same example from


our previous slides for our ease of
understanding.
Depth First Search

Let’s follow the step by step procedure to understand


this stack implementation:

Step 1: Push the starting node to stack, which is node


1 here.

Step 2: Pop the stack, the popped element here is ‘1’.

● if the popped element is not in the visited list then


add that to visited list. So, ‘1’ is added to the
visited list
● Now push all the neighbours of the popped
element, which are not in the visited list.
Therefore, 2 and 3 are pushed to the stack.
Depth First Search

Step 3: Pop the stack, the popped element here is ‘3’.

● if the popped element is not in the visited list then


add that to visited list. So, ‘3’ is added to the
visited list
● Now push all the neighbours of the popped
element, which are not in the visited list.
Therefore, 2 is pushed to the stack.

Step 4: Pop the stack, the popped element here is ‘2’.

● if the popped element is not in the visited list then


add that to visited list.So, ‘2’ is added to the
visited list
Depth First Search

● Now push all the neighbours of the popped


element, which are not in the visited list.
Therefore, 4 is pushed to the stack.

Step 5: Pop the stack, the popped element here is ‘4’.

● if the popped element is not in the visited list then


add that to visited list. So, ‘4’ is added to the
visited list
● Now push all the neighbours of the popped
element, which are not in the visited list. Since
there are no neighbours of the popped element,
which are not in the visited list. do nothing.
Depth First Search

Step 6: Pop the stack, the popped element here is ‘2’.

● if the popped element is not in the visited list then


add that to visited list. Since ‘2’ is already in the
visited list, do nothing
● Now push all the neighbours of the popped
element, which are not in the visited list.
● Since there are no neighbours of the popped
element, which are not in the visited list. do
nothing.

Since the stack is empty. The visited list is the DFS of


the graph in the first step.
Depth First Search
The following image summarizes the dry run of our new algorithm:
Breadth First Search

● BFS is a traversing algorithm where traversing starts from the start node and
then explores the immediate neighbours of the start node; then the traversing
moves towards the next-level neighbours of the graph structure.

● To implement the breadth-first search, you need to consider the stage of each
node. Nodes, in general, are considered to be in three different stages as:
○ Not visited
○ Visited
○ Completed

● In BFS algorithm, nodes are marked as visited during the traversal, in order to
avoid the infinite loops caused because of the possibilities of cycles in a
graph structure.
Breadth First Search

Let’s perform BFS on the following tree:

As you can see, we will start with all the nodes in the not visited section.
Breadth First Search

● Just like we performed DFS on a similar graph, let’s begin with 2.

● So, the first thing to do weill be to remove 2 from not visited column and add
it to the visited one.
Breadth First Search

● The next step is to visit the neighbours of 2( 1 and 4).

● Therefore, 1 and 4 are removed from the not visited column and added
to the visited section.

● After this step, 2 will be removed from the visited column and added to
completed column.
Breadth First Search

● Now, we move on to the next element in the visited column i.e. 4.

● We will follow the same procedure for 4 now and visit all its neighbours.
Breadth First Search

● Since the neighbours 1 and 2 are already been visited, we ignore them and
add the unvisited neighbours( 3 and 5) to the visited list.

● And since all the neighbours of 4 are visited now, it is removed from the
visited list and added to the completed list.
Breadth First Search

● Now we move on to the next element in the visited list i.e. 1. But, since all
the neighbours of 1 are already been visited, it is removed from the visited
column and added to the completed one.

● The same step is repeated for the next two entries in our visited column.
They are removed from the visited column and added to the completed one.
Breadth First Search

● Since the visited column is empty now, we can safely say that the BFS
traversal is complete.

● An important point to observe in this technique is that the order of


elements/nodes in the visited column and the completed column is the
same.

● It means that what comes first in the visited column, also is the first one to
get completed( FIFO nature)
Breadth First Search

Now let’s take a look at the pseudocode of BFS algorithm and analyze it.
Breadth First Search

Let’s quickly dry run the pseudocode on our example first before go in detailed
explanation of the same:

● The first step is the declaration of a queue Q


and a set for visited elements.

● Now, the starting node is added as the first


element in both the queue and the set. Let’s
start with node 2 once again.

● Now, we enter our loop...


Breadth First Search

● The loop checks if the queue is empty or not. Since it is not empty here, we
proceed with the body of the loop.

● The first step in our loop will be to dequeue the


queue element, which is 2 .

● Now, the unvisited neighbours of 2( 4 and 1) are


added to the queue as well as the set. This
completes the first loop iteration.
Breadth First Search

● The loop checks if the queue is empty or not. Since it is not empty here, we
proceed with the body of the loop once again.

● The first step in our loop will be to dequeue the


top queue element, which is 4 .

● Now, the unvisited neighbours of 4( 3 and 5) are


added to the queue as well as the set. This
completes our loop iteration.
Breadth First Search

● The loop checks if the queue is empty or not. Since it is not empty here, we
proceed with the body of the loop once again.

● If you see the graph, you can clearly see that all
the nodes have been visited at least once.

● So, when our top queue element(1) is dequeued,


it has no unvisited neighbours to be added to our
queue and set.
Breadth First Search

● The loop checks if the queue is empty or not again. Since it is not empty
here, we proceed with the body of the loop.

● We dequeue 3 and find there is no unvisited


neighbour. The loop runs again, 5 is dequeued
but again there is no unvisited neighbour.

● Now, all the elements are dequeued and Q is


empty. Thus, the loop stops running and our
traversal is complete...
Breadth First Search

The key points in our pseudocode are:

● It takes the starting node as the input parameter.

● Unlike the DFS which had a recursive logic, the BFS algorithm follows an
iterative approach.

● To maintain the FIFO relationship between visited and completed sections, a


queue is used as the core data structure to implement the logic.

Now let’s break down the pseudocode into simple steps:


Breadth First Search

Step 1: The start node is enqueued and also marked as visited in the following
set of instructions:

● enqueue (Q, n)
● Add n to visited set

Step 2: ‘While’ loop instruction set is executed when the queue is not empty

Step 3: For each iteration of while loop, a node gets dequeued

Step 4: Now ‘for’ loop runs till all the unvisited neighbours of the dequeued
node(n) are enqueued and also marked as visited.
Breadth First Search

Step 5: For the first iteration of the while loop, all the neighbour nodes of the
start node are enqueued and on the second iteration, all the next level unvisited
neighbour nodes of one of the neighbour node are enqueued.

This way, all the neighbour nodes are enqueued and visited level wise from the
start node and after a certain number of iterations, all the nodes are dequeued
and the algorithm ends.
Breadth First Search

Now let’s run our pseudocode on an another graph to further strengthen our
understanding of BFS. The following image explains all the steps by itself:
Breadth First Search

Below are the steps which explain the steps in the previous image:

Step 1: Enqueue the node ‘1’ to the queue and add it to the visited list.

Step 2: Dequeue an element from the queue, here it is ‘1’:

● Enqueue all the neighbours of the popped element, which are not in the
visited list, to the queue and also add them to the visited list.

● Enqueue 2 and 3 to the queue and add them to the visited list.
Breadth First Search

Step 3: Dequeue an element from the queue, here it is ‘2’:

● Enqueue all the neighbours of the popped element, which are not in the
visited list, to the queue and also add them to the visited list.

● Enqueue 4 to the queue and add it to the visited list.

Step 4: Dequeue an element from the queue, here it is ‘3’:

● Enqueue all the neighbours of the popped element, which are not in the
visited list, to the queue and also add them to the visited list.

● Enqueue 5 to the queue and add it to the visited list.


Breadth First Search

Step 5: Dequeue an element from the queue, here it is ‘4’:

● Enqueue all the neighbours of the popped element, which are not in the
visited list, to the queue and also add them to the visited list.

● Since there are no neighbours of ‘5’, which are not in the visited list.
Therefore, do nothing.

Step 6: Dequeue an element from the queue, here it is ‘5’:

● Enqueue all the neighbours of the popped element, which are not in the
visited list, to the queue and also add them to the visited list.
Breadth First Search

Since there are no neighbours of ‘5’, which are not in the visited list. Therefore,
do nothing.

Since the queue is empty stop.The visited list is the bfs of the graph.
Introduction to Edge Lists

● We completed both the traversal techniques for the graphs and now it’s time
to study the implementations.

● There are three key implementations of the graph abstract data structure:

○ Edge list

○ Adjacency matrix

○ Adjacency list
Introduction to Edge Lists

● Let’s start with the first implementation


called Edge List.

● As the name suggests, edge list is a


list of all the edges of the graph.

● Let’s understand it with the help of


same example we have been using:
Introduction to Edge Lists

● As we can see, there are these edges between:


○ 1 and 2
○ 1 and 4
○ 3 and 4 and so on

● So all we have to do is to maintain a list of these


edges.

● This is done so that the edge list class would


have an attribute named edges, and it’s the list of
all the edges.
Introduction to Edge Lists

● Since there is no edge connecting the node 6


to any other node, the edge list cannot
represent this node 6 in the graph.

● This is the major drawback of edge list when it


comes to unconnected graphs.

● But what is the solution to the problem? Can we


modify our implementation to incorporate the
isolated nodes as well?
Well, let’s see...
Introduction to Edge Lists

● The solution to this problem is


achieved by using an extra
data structure, a SET to
store all the nodes in the
graph.

● The new representation of the


example we have been using
is shown on the right.
● So, if have to add a new isolated node now, we just add it to the LIST
OF NODES and don’t update the EDGE LIST. Thus, we can represent
both connected and unconnected graphs now
Introduction to Edge Lists

● Let’s check how much you understood about the Edge List representation.

● What will be the edge list representation of the following graph?


Introduction to Edge Lists

Since it’s a weighted graph, we need to add another value in our edge list
object.

● Set of nodes: {0, 1, 2, 3, 4}

● Set of edges: {(0, 1, 9), (1, 2, 8), (3, 2, 6), (3, 0, 10), (4, 0, 7), (3, 4, 5)}
(0, 1, 9) indicates that there is an edge between ‘0’ and ‘1’ with edge weight
as 9.
Introduction to Adjacency Matrix

● Now let’s move on to the next representation of graph called the Adjacency
Matrix.

● As the name suggests, an adjacency matrix is a two-dimensional boolean


matrix that represents a connection between nodes.

● Here, the number of rows and the number of columns are the same and
equal to the number of nodes in the graph.

● Each row and each column corresponds to a particular node.


Introduction to Adjacency Matrix

● The logic to populate this matrix is that if there exists, an edge between the
node I and node J then you would place a true value in the cell
corresponding to the Ith row and Jth column.

● The Adjacency Matrix of the graph we’ve been using in our examples will be:
Introduction to Adjacency Matrix

● The most important aspect of any algorithm is its performance characteristics,


which determine how it will perform in a given situation or circumstance.

● Now, we will calculate the time complexity for some operations on the
adjacency matrix, specifically for the following four operations:

○ getAllNodes: Get all nodes of the graph.

○ addNode: Add anode to the graph.

○ addEdge: add an edge between the two specified nodes.

○ getAllNeighbours: Gets all neighbours of the specified node.


Introduction to Adjacency Matrix

● getAllNodes:

○ This function converts a list of nodes into a set of nodes.


○ Therefore, the time complexity is O(n).

● addNode:

○ It involves the creation of a new 2-D adjacency matrix of size n*n.


○ Then, all the contents of the previous adjacency matrix have to be
transferred in the new one.
○ Therefore, the time complexity is the product of number of rows(n) and
columns(n) i.e. O(n2).
Introduction to Adjacency Matrix

● addNode:
○ Let’s say we have to insert a new node in a graph containing only two
nodes which are joined.

○ The first step will be to create a new 3X3 matrix and copy the first two
rows and columns into it and filling the last row and column with False.

○ Now, depending on which nodes is the new node connected to, the
corresponding entry in the matrix is made True.
Introduction to Adjacency Matrix

● addEdge:

○ This function updates the adjacency matrix value to TRUE at the row and
column corresponding to the nodes between which the edge is to be
added.

○ This updation is very fast, but finding the row and column corresponding
to these nodes uses the “IndexOf’’ operation on the node list.

○ The worst case time complexity of “IndexOf” and hence, the entire
function is O(n).
Introduction to Adjacency Matrix

● addEdge:

○ For example, if an edge has to be added between nodes 1 and 2 of with


indices ‘i’ and ‘j’, the entry corresponding to ith row and jth column is set to
TRUE.
○ Also, the symmetric cell of Adjacency Matrix is also set to true.
Introduction to Adjacency Matrix

● getAllNeighbours:

○ This function also first finds, the row/column number of the given node
using IndexOf function just like addEdge.

○ In the next step, the entire row/column corresponding the node index is
traversed and all the columns/rows with entry as TRUE are shown as
neighbours.

○ Both the IndexOf function call and the row traversal for all columns take
O(n) time and hence the overall time complexity of the function is O(n) as
well.
Introduction to Adjacency Matrix

● If we analyse the space complexity of these functions, they contain an


adjacency matrix of size n2 and a list of nodes of size n. Thus, the overall
space complexity becomes O(n+n2) = O(n2). Let’s summarize:
Introduction to Adjacency Matrix

On doing the comparison of space complexities of Edge List and Adjacency


Matrix, we can say that:

Note that V represents the number of nodes/vertices and E the number of edges.
Introduction to Adjacency Matrix

This brings us to the concept of dense and sparse graphs.

● Dense graphs: A dense graph is a graph in which the number of edges is


close to the maximum possible edges for a given set of nodes.

● Sparse graphs: Sparse graphs are connected graphs with the minimum
or a small number of edges connecting nodes. In sparse graphs, there
may or may not be an edge between two nodes.

Let’s see the examples now:


Introduction to Adjacency Matrix

Which of the above is a dense graph and which one is a sparce?


Introduction to Adjacency Matrix

What will be the Adjacency Matrix representation of the following graph?


Introduction to Adjacency Matrix

Refer below for your answer:

Now, can you figure out a way to delete an edge, say from X to U?
Introduction to Adjacency Matrix

To delete the edge:

1. You have to traverse through the vertex list and find the indexes of the X and
U, here they are 3 and 0 respectively.

○ If the size of the vertex list is V then this step takes linear time which is
O(V) in the worst case.

2. Now set that (3,0)th cell to ‘0’.

○ This step takes constant time

The total time taken to delete an edge is O(V).


Next up !!

● Adjacency List

● Dijkstra's Algorithm

● Detect a cycle in a directed graph

● Topological Sorting
Introduction to Adjacency List

Now we move on to our final implementation of graph interface, Adjacency List:

● This is a list of key-value pairs or a table with each row corresponding to a


node in the graph.

● The key set will be all the nodes and the value for a key will be the set of
all its neighbours.

● As you might have guessed already, the java implementation of this


representation uses a Map.
Introduction to Adjacency List

Let’s understand it better with the help of an example:

How will you add an edge, say 2-3 in this implementation???


Introduction to Adjacency List

● To add an edge, we need to identify the two nodes which will be connected by
the edge. In our case, they will be 2 and 3.

● We need to find the first node(2) in our key set and add the second node(3) in
its value set of nodes.

● Similarly, we add the first node(2) in the value set of second node(3).

● What will be the time complexity of the addEdge operation in Adjacency List
implementation?
Introduction to Adjacency List

The following image compares performance characteristics of all the three


implementations of graph:
Introduction to Adjacency List

Further, there are a few additional points to keep in mind:

● It’s possible to improve the time performance of getAllNodes and addEdge


functions in Adjacency Matrix.
○ With proper implementation of the set of nodes, The O(V) or O(n) of
getAllNodes will drop to O(1).
○ Similarly, the performance of addEdge can be improved to O(1) as well.

● Edge Lists and Adjacency Lists are recommended for sparse graphs and
Adjacency Matrices for dense graphs on the basis of space complexities.

● The adjacency list can be concluded as behaving the most optimally, but,
again, this depends on the requirements.
Dijkstra's Algorithm

● Google Maps has become a part of our day-to-day lives; the tool comes to the
rescue whenever we want to travel.

● But, millions of people use Google Maps. So doing all the calculations to find
the shortest possible route and display the same in a couple of seconds is
surely not possible for each person who uses the tool and each request.

● So, what makes it possible?

● Well, there’s an algorithm at work behind each request that you send to
Google Maps, and it is popularly known as ‘Dijkstra’s algorithm’. Let’s learn
more about it.
Dijkstra's Algorithm

● Given a directed weighted graph ‘g’ and a


node in that graph ‘n’, it computes the
shortest distances from n to all the
other nodes of that graph.

● Dijkstra's algorithm falls in the broad


category of greedy algorithms.

● Dijkstra's algorithm will work only on


graphs with positive or non-negative
edge weights
Dijkstra's Algorithm

Well, here’s a task for you. What will be shortest distances of all the nodes from C2?
Dijkstra's Algorithm

● Now let’s try to solve the same using Dijkstra's Algorithm.

● But before we go on, you need to know the algorithm is


implemented using two core data structures:

○ Priority Queue containing all the nodes

○ Table of distances of all nodes from the source node

● Initially, all the nodes except the source node itself are
assigned maximum possible value in the table and the
source node is assigned 0.
Dijkstra's Algorithm

● The table of distances is updated using the technique called relaxation or


edge relaxation.

● The relaxation logic takes the edge we working on as the input.

● The distance value for the node say n2 is replaced when:

● This edge relaxation keeps on updating the table in a while loop until the
priority queue is empty.
Dijkstra's Algorithm

The pseudocode of the Dijkstra’s algorithm is as follows:

Procedure Dijkstra(graph, node)


Perform edge relaxation for all the outgoing edges from the
starting node
While Q is not empty
Dequeue the first element from priority queue
nextNode ← front element of queue after previous deque
if nextNode is not null
Relax the edges if necessary
end if
end while
end procedure
Dijkstra's Algorithm

Let’s break down the pseudocode into steps for better understanding:

Step 1: First perform relaxation for all the outgoing edges of the starting node.

Step 2: ‘While’ loop instruction set is executed when the queue is not empty

Step 3: For each iteration of the while loop, the node in the front gets dequeued

Step 4: nextNode will store the node in front of priority queue after the last deque

Step 5: We will check if the vale of nextNode is null or not, if It is not null we will
proceed and do edge relaxation on the nodes where require.
Dijkstra's Algorithm

Let’s continue with our problem and run the pseudocode on


the given graph :

● The first step will be to perform relaxation on all the


edges from the source node C2.

● There is only 1 such node, C1 with current value in


table as infinity.

● On performing edge relaxation, we conclude that:


Infinity > 0 + 20

● So, the value of C1 is updated as 20.


Dijkstra's Algorithm

● Now, we enter the main while loop and deque


the smallest element from our priority queue
which is C2.

● Now the next node is assigned to the smallest


value in table i.e. C1.

● Since, C1 is not null, we relax all the edges from


C1 which are C3& C4.

● For, C3, since infinity > 20 + 50, it is updated as


70 and similarly C4 is updated as 220 since
infinity > 20 + 200.
Dijkstra's Algorithm

● Now, we again check our loop condition and


since our queue is not empty, we enter the loop.

● The least value, C1 is dequeued.

● The new head node is assigned to C3 which has


two edges from it which are C4& C5.

● On relaxation of C4, since 220 > 70+70, is


updated and 140 and similarly C5 is assigned as
80 as infinity > 70+10.
Dijkstra's Algorithm

● Since the priority queue is not empty, we enter


another loop iteration.

● C3 is dequeued and C5 is assigned as the new head


node.

● There’s only one outgoing edge from C5 and it has


to be relaxed.

● Since 140 > 80 + 20, C4 is updated as 100 in the


table.
Dijkstra's Algorithm

● Since the priority queue is still not empty, we enter


another loop iteration.

● C5 is dequeued and C4 is assigned as the new head


node.

● There’s only one outgoing edge from C4 which is C2.

● But, since we have already removed C2 from the


priority queue, there’s no relaxation to be done.
Detect Cycle in a Directed Graph

Let’s look at a problem now. You are given a directed graph. Your task is to
find out whether the graph contains a cycle or not.

For example, observe the given directed


graph. It clearly shows that there is a cycle
present in this graph: 1-> 2 -> 3 -> 1
Detect Cycle in a Directed Graph

Let’s discuss the approach to solve this problem:

● This problem can be solved using a depth-first search (DFS). For each
node in a graph, you need to perform depth-first traversal.

● While performing depth-first traversal, if you reach the same node from
where you started, then it means that a cycle exists in the graph.

● If you are unable to reach the same node for any of the nodes in the graph,
then it means that a cycle does not exist.
Topological Sorting

Topological sorting for a directed acyclic graph (DAG) is a linear ordering of


vertices such that for every directed edge u -> v, vertex u comes before v in the
ordering. Topological sorting for a graph is not possible if the graph is not a
DAG.

For example, a topological sorting of the following


graph is {5 4 2 3 1 0}.

There can be more than one topological sorting for


a graph. For example, another topological sorting
of the graph above is {4 5 2 3 1 0}.
Topological Sorting

The first vertex in a topological sorting is always a vertex with an in-degree as 0


(a vertex with no incoming edges).

We can find topological sorting using both DFS and BFS. First, let’s see how
you can find a topological sorting using DFS:

● Create a temporary stack.

● Start from a vertex and recursively call topological sorting for all its adjacent
vertices, then push it to the stack (when all adjacent vertices are on stack).

● Step 3: Finally, print contents of stack.


Topological Sorting

Now, let’s discuss topological Sorting using BFS:

● Compute the in-degree for each of the vertex present in the DAG.

● Pick all the vertices with in-degree as 0 and add them into a queue.

● Remove a vertex from the queue and add it to the result array.Then,
decrease in-degree by 1 for all its neighbouring nodes. If in-degree of a
neighbouring nodes is reduced to zero, then add it to the queue.

● Repeat Step 3 until the queue is empty.


Topological Sorting
Algorithm for Topological Sorting using Breadth-First Search:
topological_sort(n,adj[n][n]):

t = []

visited = []

in_degree = []

for i = 0 to n

in _degree[i] = visited[i] = 0

for i = 0 to n

for j = 0 to n

If adj[i][j] is True

in_degree+=1
Topological Sorting
for i = 0 to n

If in_degree is 0

enqueue(Queue,I)

visited[i] = True

while Queue is not Empty

vertex = get_front(Queue)

dequeue(Queue)

t.append(vertex)

for j = 0 to n

if adj[vertex] is True and visited[] is False


Topological Sorting

in_degree -=1

if in_degree is 0

enqueue(Queue,j)

visited[j] = True

return t

Complexity Analysis:

Time complexity: O(V+E)

Space complexity: O(V)

V is the number of vertices in the graph, and E is the number of edges in the graph.
Thanks for
Listening!

You might also like