Unit III
Unit III
GRAPH
Graphs - Basic Concepts
• Non-linear data structures are used to represent the data containing a network or
• Graphs are one of the most important non linear data structures.
• Graphs as non-linear data structures represent the relationship among data elements,
• A graph G is a collection of nodes (vertices) and arcs joining a pair of the nodes
(edges). Edges between two vertices represent the relationship between them.
• For finite graphs,V and E are finite. We can denote the graph as G = (V, E).
• Graph in Data Structure is a non-linear data structure that consists of nodes and edges
• A Graph in Data Structure is a collection of nodes that consists of data and are
Term Description
Multigraph -
A graph having multiple edges to join the same vertices
or nodes.
Undirected Graph -
Advantages:
• Easy to store and manipulate matrices.
• Finding the cycles and path in a graph is easy.
Disadvantage:
• The space requires for a graph having N vertices is N X N, but the matrix
contain only few elements.
Adjacency Matrix - Algorithm
class Graph {
private:
int Adj_Matrix[Max_Vertex][Max_Vertex]; // Adjacency matrix
int Vertex; // Number of vertices
int Edge; // Number of edges
public:
Graph(); // Constructor
bool IsEmpty();
void Insert_Vertex(int u);
void Insert_Edge(int u, int v);
bool IsEdge(int u, int v);
void Remove_Edge(int u, int v);
void Display();
void DFS(int startVertex);
};
C++ Graph Implementation Using Adjacency matrix
// Constructor
Graph::Graph()
{
Vertex = 0;
Edge = 0;
Vertex++;
}
Adj_Matrix[u][v] = 1;
Adj_Matrix[v][u] = 1; // Assuming an undirected graph
Edge++;
}
C++ Graph Implementation Using Adjacency matrix
// Add vertices
graph.Insert_Vertex(0);
graph.Insert_Vertex(1);
graph.Insert_Vertex(2);
graph.Insert_Vertex(3);
// Add edges
graph.Insert_Edge(0, 1);
graph.Insert_Edge(0, 2);
graph.Insert_Edge(1, 3);
graph.Insert_Edge(2, 3);
return 0;
}
Adjacency List Representation of Graph - Algorithm
return 0;
}
Multi-list Representation
• In the Multi- list representation of graph structures; these are two parts,
• There is one entry in the node directory for each node of the graph.
• The directory entry for node i points to a linked adjacency list for node i.
• Each record of the linked list area appears on two adjacency lists: one for the node at
represented by two entries, one on the list of vi and the other on the list of vj .
• For the graph G1 in Fig., the edge connecting the vertices 1 and 2 is represented
twice, in the lists of vertices 1 and 2.
• For each edge, there will be exactly one node, but this node will be in two lists, that
is, the adjacency lists for each of the two nodes it is incident on.
• The node structure of such a list can be represented as follows:
• Here, the visited tag is a one bit mark field that indicates whether or not the edge has
been examined.
• This tag would be set accordingly when the edge is processed.
Inverse adjacency list
• An inverse adjacency list is a set of lists that contains one list for each vertex.
• Each list contains a node per vertex adjacent to the vertex it represents.
Graph Traversal -
• To solve many problems modelled with graphs, we need to visit all the vertices and
edges in a systematic fashion called graph traversal.
• There are two types of graph traversal
1. Depth-first traversal and
2. Breadth-first traversal.
• Traversal of a graph is commonly used to search a vertex or an edge through the
graph; hence, it is also called a search technique.
• Consequently, depth-first and breadth-first traversals are popularly known as
depth-first search (DFS) and breadth-first search (BFS), respectively.
Graph Traversal - Depth-first Search (DFS)
say v of G as a start vertex; v is marked as visited. Then, each unvisited vertex adjacent
to v is searched using the DFS recursively. Once all the vertices that can be reached
from v have been visited, the search for v is complete. If some vertices remain unvisited,
we select an unvisited vertex as a new start vertex and then repeat the process until all
(vertex or node address) onto the stack. We would then pop the vertex, process it, and
// number of edges
int e = 4;
// adjacency matrix
adj = vector<vector<int> >(v,vector<int>(v, 0));
addEdge(0, 1);
addEdge(0, 2);
addEdge(0, 3);
addEdge(0, 4);
// Perform DFS
dfs(0, visited);
}
Graph Traversal - Depth-first Search (DFS) For recursive implementation
}
// Function to perform DFS traversal
void DFS(int vertex)
{
vector<bool> visited(totalVertices, false); // Visited flag
// array
DFSUtil(vertex, visited);
}
int main() {
int vertices = 8;
Graph graph(vertices);
graph.addEdge(1, 5);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(3, 6);
graph.addEdge(2, 7);
graph.addEdge(2, 4);
cout << "The Adjacency List of the Graph is:" << endl;
// Display function can be implemented here for visualization
return 0;
}
Graph Traversal - Depth-first Search (DFS) For non - recursive implementation
DFS (Depth First Search)
while (!stack.empty()) {
int v = stack.top();
stack.pop();
if (!visited[v]) {
cout << v << " -> ";
visited[v] = true;
• Breadth First Search (BFS) is a fundamental graph traversal algorithm. It involves visiting
• Breadth First Search (BFS) is a graph traversal algorithm that explores all the
vertices in a graph at the current depth before moving on to the vertices at the next
depth level. It starts at a specified vertex and visits all its neighbors before moving on
Graph(int V) : V(V) {
adjLists.resize(V, nullptr); // Allocate space for adjacency
lists
}
// Function for adding an edge to the adjacency matrix
} else {
}
// Function for Breadth-First Search traversal using adjacency matrix
void bfs(const vector<vector<int>>& adjMatrix, int start, int V) {
vector<bool> visited(V, false); // Keeps track of visited nodes
queue<int> q; // Queue for BFS traversal
while (!q.empty()) {
int current_node = q.front();
q.pop();
cout << current_node << " -> "; // Visit the current node
// Iterate through adjacent node (using matrix row for current node)
for (int i = 0; i < V; i++) {
if (adjMatrix[current_node][i] == 1 && !visited[i]) {
q.push(i); // Enqueue unvisited adjacent node
visited[i] = true; // Mark neighbor as visited
}
}
}
cout << endl;
}
int main() {
// Sample adjacency matrix (initially no edges)
addEdge(adjMatrix, 0, 1);
addEdge(adjMatrix,0, 2);
addEdge(adjMatrix,1, 2);
addEdge(adjMatrix,2, 0);
addEdge(adjMatrix,2, 3);
addEdge(adjMatrix,3, 4);
cout << "Visited nodes (BFS): ";
bfs(adjMatrix, 0, V); // Start BFS from node 0
return 0;
}
// Function to perform BFS traversal on the graph using adjacency List
tree such that the sum of all the weights of the spanning tree is minimum.
Greedy algorithms for computing minimum spanning tree
• The two popular methods used to compute the minimum spanning tree of a graph are
1. Prim’s algorithm
2. Kruskal’s algorithm
Prim’s Algorithm
To implement Prim’s algorithm in finding the minimum spanning tree of a graph, here are
the things to be in mind:
1. All the vertices of the graph must be included
2. The vertex with the minimum weight(distance) must be selected first. All the vertices
must be connected
3. There must be no cycle
Prim’s Algorithm Steps
Step-1:
1. Randomly choose any vertex.
2. The vertex connecting to the edge having least weight is usually selected.
Step-2:
3. Find all the edges that connect the tree to new vertices.
4. Find the least weight edge among those edges and include it in the existing tree.
5. If including that edge creates a cycle, then reject that edge and look for the next
least weight edge.
Step-3:
6. Keep repeating step-2 until all the vertices are included and Minimum Spanning
Tree (MST) is obtained.
Algorithm for Prim's Minimum Spanning Tree
Below we have the complete logic, stepwise, which is followed in prim's algorithm:
Step 1: Keep a track of all the vertices that have been visited and added to the spanning
tree.
Step 2: Create the spanning tree and Initially the spanning tree is empty.
Step 3: Choose a random vertex, and add it to the spanning tree. This becomes the root
node.
Step 5: Repeat the Step 4, till all the vertices of the graph are added to the spanning tree.
#include<iostream>
using namespace std;
// Number of vertices in the graph
const int V=6;
// Function to find the vertex with minimum key value
int min_Key(int key[], bool visited[])
{
int min = 999, min_index; // 999 represents an Infinite value
return 0;
}
Example for Prim's Minimum Spanning Tree Algorithm
Let's try to trace the above algorithm for finding the Minimum Spanning Tree for the graph
in Fig
Step A:
• Define key[] array for storing the key value(weight
or distance or cost) of every vertex. Initialize this to
∞(infinity) for all the vertices.
• Define another array Boolean visited[] for keeping a
track of all the vertices that have been added to the
spanning tree. Initially this will be 0 for all the
vertices, since the spanning tree is empty.
• Define an array parent[] for keeping track of the
parent vertex. Initialize this to -1 for all the vertices.
• Initialize minimum cost, minCost = 0
Step B:
Choose any random vertex, say f and set key[f]=0.
minCost=6+3=9
Then, e is selected and the minimum cost will become, minCost=9+2=11
• Since all the vertices have been visited now, the algorithm terminates.
• Thus, Fig. 21 represents the Minimum Spanning Tree with total cost=11.
Minimum Spanning Tree Using Kruskal Algorithm
• Kruskal's algorithm for finding the Minimum Spanning Tree(MST), which finds an edge
of the least possible weight that connects any two trees in the forest
• It is a greedy algorithm.
• It finds a subset of the edges that forms a tree that includes every vertex, where the
• If the graph is not connected, then it finds a minimum spanning forest (a minimum
2. Pick the edge with the least weight. Check if including this edge in spanning tree will
3. Repeat the step 2 till spanning tree has V-1 (V – no of vertices in Graph).
4. Spanning tree with least weight will be formed, called Minimum Spanning Tree
Union-Find Disjoint Sets data structure
// Initialize result
mst_weight = 0
// Create V single item sets
for each vertex v
parent[v] = v;
rank[v] = 0;
Sort all edges into non decreasing order by weight w
for each (u, v) taken from the sorted list E
do if FIND-SET(u) != FIND-SET(v)
print edge(u, v)
mst_weight += weight of edge(u, v)
UNION(u, v)
using namespace std;
// Constructor
Graph(int V, int E)
{
this->V = V;
this->E = E;
}
// Constructor.
DisjointSets(int n)
{
// Allocate memory
this->n = n;
parent = new int[n+1];
rnk = new int[n+1];
// Union by rank
void merge(int x, int y)
{
x = find(x), y = find(y);
if (rnk[x] == rnk[y])
rnk[y]++;
}
};
/* Functions returns weight of the MST*/
int Graph::kruskalMST()
{
int mst_wt = 0; // Initialize result
sort(edges.begin(), edges.end()); // Sort edges in increasing order on basis of cost
DisjointSets ds(V); // Create disjoint sets
vector< pair<int, iPair> >::iterator it; // Iterate through all sorted edges
for (it=edges.begin(); it!=edges.end(); it++)
{
int u = it->second.first; // iterators second members first element
int v = it->second.second; // iterators secong members second element
int set_u = ds.find(u);
int set_v = ds.find(v);
//Check if the selected edge is creating a cycle or not
//Cycle is created if u and v belong to same set
if (set_u != set_v)
{
cout << u << " - " << v << endl; //Current edge will be in the MST so print it
mst_wt += it->first; // Update MST weight
ds.merge(set_u, set_v); // Merge two sets
}
}
// Driver program to test above functions
int main()
{
/* Let us create above shown weighted
and undirected graph */
int V = 9, E = 14;
Graph g(V, E);
return 0;
}
Dikjtra's Single source shortest path
• Dijkstra Algorithm is an algorithm for finding the shortest path from a source node
weights.
graph.
representation of graph, where E is the number of edges in the graph and V is the
• Dijkstra's Algorithm basically starts at the node that you choose (the source node) and
it analyzes the graph to find the shortest path between that node and all the other
• The algorithm keeps track of the currently known shortest distance from each node to
the source node and it updates these values if it finds a shorter path.
• Once the algorithm has found the shortest path between the source node and another
• The process continues until all the nodes in the graph have been added to the path.
This way, we have a path that connects the source node to all other nodes following
1. Mark the source node with a current distance of 0 and the rest with infinity.
2. Set the non-visited node with the smallest current distance as the current node.
3. For each neighbor, N of the current node adds the current distance of the adjacent
node with the weight of the edge connecting 0->1. If it is smaller than the current
distance[source] = 0
G = the set of all nodes of the Graph
while G is non-empty:
Q = node in G with the least dist[ ]
mark Q visited
for each neighbor N of Q:
alt_dist = distance[Q] + dist_between(Q, N)
if alt-dist < distance[N]
distance[N] := alt_dist
Example of Dijkstra's Algorithm
Generate the shortest path from node 0 to all the other nodes in the graph.
Since we are choosing to start at node 0, we can mark this node as visited.
Now we need to start checking the distance from node 0 to its adjacent nodes. As you
can see, these are nodes 1 and 2 (see the red edges):
After updating the distances of the adjacent nodes, we need to:
1. Select the node that is closest to the source node based on the current known
distances.
2. Mark it as visited.
3. Add it to the path.
If we check the list of distances, we can see that node 1 has the shortest distance to the
source node (a distance of 2), so we add it to the path.
For node 3: the total distance is 7 because we add the weights of the edges that form the
path 0 -> 1 -> 3
Now that we have the distance to the adjacent nodes, we have to choose which node will
be added to the path. We must select the unvisited node with the shortest (currently
known) distance to the source node.
From the list of distances, we can immediately detect that this is node 2 with distance 6:
We also mark it as visited in the list of distances and crossing it off from the list of
unvisited nodes:
Now we need to repeat the process to find the shortest path from the source node to
the new adjacent node, which is node 3.
You can see that we have two possible paths 0 -> 1 -> 3 or 0 -> 2 -> 3.
We need to check the new adjacent nodes that we have not visited so far. This time, these
nodes are node 4 and node 5 since they are adjacent to node 3.
We update the distances of these nodes to the source node, always trying to find a shorter
path, if possible:
• For node 4: the distance is 17 from the path 0 -> 1 -> 3 -> 4.
• For node 5: the distance is 22 from the path 0 -> 1 -> 3 -> 5.
We need to choose which unvisited node will be marked as visited now. In this case, it's
node 4 because it has the shortest distance in the list of distances. We also mark it as
"visited" by adding a small red square in the list. And we cross it off from the list of
unvisited nodes.
And we repeat the process again. We check the adjacent nodes: node 5 and node 6. We
need to analyze each possible path that we can follow to reach them from nodes that
have already been marked as visited and added to the path.
For node 5:
1. The first option is to follow the path 0 -> 1 -> 3 -> 5, which has a distance of 22 from
the source node (2 + 5 + 15). This distance was already recorded in the list of
distances in a previous step.
2. The second option would be to follow the path 0 -> 1 -> 3 -> 4 -> 5, which has a
distance of 23 from the source node (2 + 5 + 10 + 6).
3. Clearly, the first path is shorter, so we choose it for node 5
For node 6:
The path available is 0 -> 1 -> 3 -> 4 -> 6, which has a distance of 19 from the source
node (2 + 5 + 10 + 2).
There are three different paths that we can take to reach node 5 from the nodes that have
been added to the path:
return min_index;
}
//Function to print the constructed distance array
void printSolution(int distance[])
{
cout <<"Vertex \t Distance from Source" << endl;
for (int i = 0; i < V; i++)
cout << (char)(i+65) << " \t\t"<<distance[i]<< endl;
}
// Function that implements Dijkstra's algorithm
void dijkstra(int graph[V][V], int src)
{
int distance[V]; //initializing output array
bool sptSet[V]; // list of visited nodes
// Initializing all distances as INFINITE and sptSet[] as false
for (int i = 0; i < V; i++)
distance[i] = INT_MAX, sptSet[i] = false;
// Setting distance of source as 0
distance[src] = 0;
// Find shortest path for all vertices
for (int count = 0; count < V - 1; count++) {
//calling minDistance to pick next vertex
int u = minDist(distance, sptSet);
// Mark the picked vertex as visited
sptSet[u] = true;
//Relaxing all neighbours of U
for (int v = 0; v < V; v++)
if (!sptSet[v] && graph[u][v] && distance[u] != INT_MAX
&& distance[u] + graph[u][v] < distance[v])
distance[v] = distance[u] + graph[u][v];
}
// print the constructed distance array
printSolution(distance);
}
// driver function
int main()
{
//Example graph
//Same as Graph in example diagram above
return 0;
}
All pairs shortest paths- Floyd-Warshall Algorithm
• The Floyd Warshall Algorithm is an algorithm that solves the all-pairs shortest path
• The algorithm calculates the shortest path between all pairs of vertices in a graph.
• It is unique because it can handle negative edge weights and graphs with cycles.
• The algorithm works by constructing a matrix of the shortest distances between all
• The algorithm repeats this process for all vertices in the graph, and eventually, the
resulting matrix will contain the shortest distance between all pairs of vertices.
Algorithm : Floyd-Warshall
1. Create a two-dimensional array of size n x n for storing the length of the shortest path between all
node pairs.
n is the number of nodes in the graph.
Initialize all the cells of this array with ∞.
2. For every node i in the graph, initialize the distance of the node to itself as 0.
Distance [ i ] [ i ] = 0.
3. For every edge ( u, v ) in the graph, initialize the distance array as the weight of the edge.
Distance [ u ] [ v ] = Weight of (u, v)
4. For every node k in [ 1 … n ] do
For every node i in [ 1 … n ] do
For every node j in [ 1 … n ] do
If ( Distance [ i ] [ j ] > Distance [ i ] [ k ] + Distance [ k ] [ j ] ) :
Distance [ i ] [ j ] = Distance [ i ] [ k ] + Distance [ k ] [ j ]
Advantages and Disadvantages
Advantages:
1. The Floyd Warshall Algorithm is easy to understand and implement.
2. It can handle negative edge weights and graphs with cycles.
3. It can solve problems involving a large number of nodes in the graph.
Disadvantages:
1. The algorithm has a high time complexity of O(V³), where V is the number of vertices in
the graph.
2. The algorithm requires a large amount of memory to store the distance matrix.
3. The algorithm does not work well for very large graphs.
Example Walkthrough
Let’s walk through an example using a simple graph with 4 nodes (A, B, C, D) and the
following weighted edges:
A to B: 3
A to D: 7
B to C: 1
C to D: 2
D to B: 5
Iteration with k = A:
Iteration with k = B:
Updated matrix:
Iteration with k = D:
Update distances considering D as an intermediate node:
• dist[A][B] and dist[C][B] remain unchanged as there are no shorter paths through
D.
#include <iostream>
#include <vector>
#include <bits/stdc++.h>
addEdge(dist, 0, 1, 3);
addEdge(dist, 0, 4, -4);
addEdge(dist, 0, 2, 8);
addEdge(dist, 1, 3, 1);
addEdge(dist, 1, 4, 7);
addEdge(dist, 2, 1, 4);
addEdge(dist, 3, 0, 2);
addEdge(dist, 3, 2, -5);
addEdge(dist, 4, 3, 6);
for (int i=0; i < V; i++)
dist[i][i] = 0;
floyd_warshall(dist, V);
cout << "Shortest distance between all pairs: " << endl;
for (int i=0; i < V; i++)
for (int j=0; j < V; j++)
cout << i << "," << j << " : " << dist[i][j] << endl;
cout << endl;
return 0;
}
Topological Sort/Ordering
• Topological sorting for 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 sort is a graph traversal that every node in the graph is visited until all of its
• Only directed acyclic graph(DAG) can have topological orderings which can align all the
1. Only applicable to directed acyclic graphs (DAGs), not suitable for cyclic graphs.
Algorithm:
1. Add all nodes with in-degree 0 to a queue.
2. While the queue is not empty:
3. Remove a node from the queue.
4. For each outgoing edge from the removed node, decrement the in-degree of the
destination node by 1.
5. If the in-degree of a destination node becomes 0, add it to the queue.
6. If the queue is empty and there are still nodes in the graph, the graph contains a
cycle and cannot be topologically sorted.
7. The nodes in the queue represent the topological ordering of the graph.
Kahn’s algorithm for Topological Sorting
Kahn’s algorithm for Topological Sorting
return result;
}