Introduction to Graphs
Graph is a non linear data structure. A graph contains a set of points known as nodes (or vertices) and set of links
known as edges (or Arcs) which connects the vertices.
A graph is defined as Graph is a collection of vertices and arcs which connects vertices in the graph. A graph G is
represented as G = ( V , E ), where V is set of vertices and E is set of edges.
Example: graph G can be defined as G = ( V , E ) Where V = {A,B,C,D,E} and
E = {(A,B),(A,C)(A,D),(B,D),(C,D),(B,E),(E,D)}. This is a graph with 5 vertices and 6 edges.
Graph Terminology
[Link] : An individual data element of a graph is called as Vertex. Vertex is also known as node. In above
example graph, A, B, C, D & E are known as vertices.
[Link] : An edge is a connecting link between two vertices. Edge is also known as Arc. An edge is represented
as (starting Vertex, ending Vertex).
In above graph, the link between vertices A and B is represented as (A,B).
Edges are three types:
[Link] Edge - An undirected edge is a bidirectional edge. If there is an undirected edge between vertices
A and B then edge (A , B) is equal to edge (B , A).
[Link] Edge - A directed edge is a unidirectional edge. If there is a directed edge between vertices A and B
then edge (A , B) is not equal to edge (B , A).
[Link] Edge - A weighted edge is an edge with cost on it.
Types of Graphs
[Link] Graph
A graph with only undirected edges is said to be undirected graph.
[Link] Graph
A graph with only directed edges is said to be directed graph.
1
3. Weighted Graph
A graph is said to be weighted if there are some non negative value assigned to each edges of the graph. The
value is equal to the length between two vertices. Weighted graph is also called a network.
Graph Representations
Graph data structure is represented using following representations.
1. Adjacency Matrix 2. Adjacency List
[Link] Matrix
In this representation, graph can be represented using a matrix of size total number of vertices by total number of
vertices; means if a graph with 4 vertices can be represented using a matrix of 4X4 size.
In this matrix, rows and columns both represent vertices.
This matrix is filled with either 1 or 0. Here, 1 represents there is an edge from row vertex to column vertex and 0
represents there is no edge from row vertex to column vertex.
Adjacency Matrix : let G = (V, E) with n vertices, n 1. The adjacency matrix of G is a 2-dimensional n n
matrix, A, A(i, j) = 1 iff (vi, vj) E(G) ( vi, vj for a diagraph), A(i, j) = 0 otherwise.
Example : for undirected graph
For a Directed graph
The adjacency matrix for an undirected graph is symmetric; the adjacency matrix for a digraph need not be
symmetric.
The space needed to represent a graph using adjacency matrix is n2 bits. To identify the edges in a graph,
adjacency matrices will require at least O(n2) time.
2. Adjacency List
In this representation, every vertex of graph contains list of its adjacent vertices. The n rows of the adjacency
matrix are represented as n chains. The nodes in chain I represent the vertices that are adjacent to vertex i.
2
It can be represented in two forms. In one form, array is used to store n vertices and chain is used to store its
adjacencies. Example:
So that we can access the adjacency list for any vertex in O(1) time. Adjlist[i] is a pointer to to first node in the
adjacency list for vertex i. Structure is
#define MAX_VERTICES 50
typedef struct node *node_pointer;
typedef struct node { int vertex;
struct node *link;
};
node_pointer graph[MAX_VERTICES];
int n=0; /* vertices currently in use */
Another type of representation is given below. example: consider the following directed
graph representation implemented using linked list
This representation can also be implemented using array
Sequential representation of adjacency list is
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
9 11 13 15 17 18 20 22 23 2 1 3 0 0 3 1 2 5 6 4 5 7 6
Graph
Instead of chains, we can use sequential representation into an integer array with size n+2e+1. For 0<=i<n,
Array[i] gives starting point of the list for vertex I, and array[n] is set to n+2e+1. The adjacent vertices of node I
are stored sequentially from array[i].
For an undirected graph with n vertices and e edges, linked adjacency list requires an array of size n and 2e chain
nodes. For a directed graph, the number of list nodes is only e. the out degree of any vertex may be determined by
3
counting the number of nodes in its adjacency list. To find in-degree of vertex v, we have to traverse complete
list.
To avoid this, inverse adjacency list is used which contain in-degree.
4
Graphs:
A Graph is a non-linear data structure that consists of vertices (nodes) and
[Link] is represented as G[V,E].
The two most common ways a Graph can be traversed are:
Depth First Search (DFS)
Breadth First Search (BFS)
Depth-First Search (DFS) is a fundamental algorithm used for traversing or
searching through data structures like graphs and trees. DFS uses a stack data
structure .It explores as far down a branch as possible before backtracking,
making it useful for tasks where exploring deeply connected nodes or paths is
important.
Steps for DFS:
1. Select the start [Link] the start node.
2. Explore the neighbours of the start node.
3. If there are undiscovered neighbour nodes, switch to that node
and repeat the steps (recursively or using a stack data structure).
4. When there are no undiscovered neighbour nodes, go back and
return to the previous node.
5. Repeat the steps until all nodes have been visited.
Algorithm:
Void dfs(Node v){
if(v == null)
return;
if(v not yet visited)
Visit&mark(v); //visit node before adjacent nodes
for(each w adjacent to v)
if(w has not yet been visited)
dfs(w);
}//dfs
EXAMPLE:
Breadth-First Search (BFS) is a fundamental algorithm used to traverse or
search through data structures like graphs and trees. It uses Queue data
[Link] systematically explores the nodes level by level, starting from a
given source node, and visits all nodes at the current depth level before moving
on to nodes at the next depth level.
The steps for a breadth first search (BFS) are:
1. Start with an empty queue and visited array.
2. Select a source node and mark it as visited.
3. Visit neighbours: Remove the first node from the queue and visit all its
unvisited neighbours.
4. Add neighbours to queue: Mark the visited neighbours and add them to
the queue.
5. Repeat steps 2–4 until the queue is empty.
Algorithm:
void bfs (Node v){
if(v == null){
return;
enqueue(v);
while(queue is not empty){
dequeue(v);
for(each w adjacent to v)
if(w has not yet been visited && has not been queued
enqueue(w));
}//while
EXAMPLE:
BICONNECTED COMPONENTS
A graph with no articulation point is called
“biconnectd”.Biconnected components are subgraphs
formed after removal of articulation point,they are such
components in which removal of any node or the edges
incident on that node will not disconnect the subgraph
[Link] other words the graph formed after removal
of all the articulation points and bridges contains only
biconnected components(all disjoint).
Biconnected components only have articulation points
in [Link] non recursive algorithmic approach is
used to find the biconnected components.
EXAMPLE:
A
B
C E
D
In above figure B is the articulation [Link] removal
of B the graph becomes [Link] graph left
with 4 disjoint components A,C,D,[Link] of them is
biconnected component.
A E
C D
TIME COMPLEXITY: O(N+E),Where N is the number of
nodes and E is the number of edges,as the time
complexity of Depth first search.
SPACE COMPLEXITY:O(N),Here N is the number of
nodes in the graph,we need a O(N) recursion stack
space to make DFS calls.
ALGORITHM:
STEP 1: Perform DFS and compute dfsnum by visiting
all the vertices.
STEP 2: Find low value and find articulation points by
checking condition low[w]>=dfsnum[u] for each(u,w).
STEP 3: Remove the articulation point.
STEP 4:Use a stack to keep track of the biconnected
component currently being traversed.
PROPERTIES:
Every non-bridge edge belongs to exactly one
biconnected component.
Biconnected components can overlap but are generally
disjoint or share only vertices.
CHARACTERISTICS OF BICONNECTED COMPONENETS
[Link]: Each biconnected component is a
connected subgraph.
[Link] Cut Vertices: A biconnected component does not
have any cut vertices(also known as articulation point).
3. No Bridges: A biconnected component does not
have any bridges.
[Link]: Biconnected components contain [Link] a
graph is a tree(i.e.,has no cycles),it has no biconnected
components.
[Link] subgraphs: Biconnected components are
maximal subgraphs,meaning that they cannot be
extended by adding more vertices or edges without
introducing cut vertices or bridges.
APPLICATION:
Biconnected graphs are used in the design of power
grid networks and communication networks.
Introduction to Divide and Conquer Algorithm
Divide and Conquer Algorithm
Divide and Conquer Algorithm is a problem-solving technique used to solve problems by dividing
the main problem into subproblems, solving them individually and then merging them to find
solution to the original problem. In this article, we are going to discuss how Divide and Conquer
Algorithm is helpful and how we can use it to solve problems.
PICTORIAL REPRESENTAION
Divide and Conquer Algorithm Definition:
Divide and Conquer Algorithm involves breaking a larger problem into smaller subproblems,
solving them independently, and then combining their solutions to solve the original problem. The
basic idea is to recursively divide the problem into smaller subproblems until they become simple
enough to be solved directly. Once the solutions to the subproblems are obtained, they are then
combined to produce the overall solution.
Working of Divide and Conquer Algorithm(process)
Divide and Conquer Algorithm can be divided into three steps: Divide, Conquer and Merge .
STEP 1:-Divide.
STEP 2:-Conquer.
STEP 3:-Merge(combine).
Introduction to Divide and Conquer Algorithm
1. Divide:
*Break down the original problem into smaller subproblems.
*Each subproblem should represent a part of the overall problem.
*The goal is to divide the problem until no further division is possible.
2. Conquer:
*Solve each of the smaller subproblems individually.
*If a subproblem is small enough (often referred to as the “base case”), we solve it directly without
further recursion.
*The goal is to find solutions for these subproblems independently.
3. Merge:
*Combine the sub-problems to get the final solution of the whole problem.
*Once the smaller subproblems are solved, we recursively combine their solutions to get the
solution of larger problem.
*The goal is to formulate a solution for the original problem by merging the results from the
subproblems.
Introduction to Divide and Conquer Algorithm
Divide and Conquer logical algorithm
Time complexity
*Time complexity of the divide and conquer algorithm is O(nLogn) for all cases: best, average and
worst .
Introduction to Divide and Conquer Algorithm
General Method
The General Method refers to a broader approach to problem-solving that can encompass various
strategies, including but not limited to Divide and Conquer. It involves:
Understanding the Problem: Clearly define the problem and identify the requirements.
Planning: Devise a strategy or algorithm to solve the problem. This could involve breaking the
problem down (like in Divide and Conquer), using dynamic programming, greedy algorithms, etc.
Implementation: Write the code or steps to execute the plan.
Testing and Debugging: Verify that the solution works correctly and efficiently. Debug any
issues that arise.
Optimization: Improve the solution for better performance if necessary.
Example:
Dynamic Programming
Understanding the Problem: Identify overlapping sub-problems and optimal substructure.
Planning: Create a table to store solutions to sub-problems.
Implementation: Fill the table based on the recurrence relation.
Testing and Debugging: Ensure the table is filled correctly and the final solution is accurate.
Optimization: Optimize space complexity if possible.
Introduction to Divide and Conquer Algorithm
Applications of the Divide and Conquer approach include
Merge Sort: A classic sorting algorithm.
Median Finding: Used to find the median of a set of numbers.
Min and Max Finding: Can find both the minimum and maximum elements in
an array simultaneously.
Binary Search.
Quick Sort.
Strassen’s Matrix Multiplication.
Karatsuba Algorithm.
Cooley-Tukey Fast Fourier Transform (FFT) Algorithm.
Karatsuba Algorithm for Fast Multiplication.
Merge sort is a divide-and-conquer algorithm
based on the idea of breaking down a list into
several sub-lists until each sublist consists of a
single element and merging those sublists in a
manner that results into a sorted list.
Idea:
● Divide the unsorted list into 𝑁 sublists, each
containing 1 element.
● Take adjacent pairs of two singleton lists and
merge them to form a list of 2 elements.
● 𝑁 will now convert into 𝑁/2 lists of size 2.
● Repeat the process till a single sorted list is
obtained.
While comparing two sublists for merging, the first
element of both lists is taken into consideration.
While sorting in ascending order, the element that
is of a lesser value becomes a new element of the
sorted list. This procedure is repeated until both
the smaller sublists are empty and the new
combined sublist comprises all the elements of
both the sublists.
As one may understand from the image above, at
each step a list of size 𝑀 is being divided into
sublists of size 𝑀/2, until no further division can be
done. To understand better, consider a smaller
array 𝐴 containing the elements (9,7,8)
At the first step this list of size 3 is divided into 2
sublists the first consisting of elements (9,7) and
the second one being (8)
Now, the first list consisting of elements (9,7) is
further divided into 2 sublists consisting of
elements (9)and (7)respectively.
As no further breakdown of this list can be done,
as each sublist consists of a maximum of 1
element, we now start to merge these lists. The 2
sub-lists formed in the last step are then merged
together in sorted order using the procedure
mentioned above leading to a new list (7,9)
Backtracking further, we then need to merge the
list consisting of element (8) too with this list,
leading to the new sorted list (7,8,9)
Time Complexity:
The list of size 𝑁 is divided into a max of 𝑙𝑜𝑔𝑁
parts, and the merging of all sublists into a single
list takes 𝑂(𝑁) time, the worst case run time of this
algorithm is 𝑂(𝑁𝐿𝑜𝑔𝑁)
Quick Sort
• Quick sort works on the principle of divide and
conquer method
• It’s a popular algorithm useful for efficiency of
sorting the elements
• Time complexity cases:
Best average worst
O(nlogn) O(nlogn) O(n^2)
• How does quick sort works:
1. Choose a pivot (always choose first element as
pivot)
2. Partition the array around pivot. After partition,
it is ensured that all elements are smaller than all
right and we get index of the end point of smaller
elements. The left and right may not be sorted
individually.
3. Recursively call for the two partitioned left and
right subarrays.
4. We stop recursion when there is only one
element is left.
• Representation of quick sort elements:
partition 1 pivot partition 2
partition 1 : has elements less than the pivot
partition 2 : has elements greater than the pivot
❖Key points for swapping an elements
• If in elements left and right position elements are
found then swap the both positions(only if left and
right position in order)
• If right position comes first then swap the right
position with pivot(only if right position comes first
before left position)
➢ Let us know about quick sort with an example:
12 4 21 30 25 17 5 8
Step-1:
• Take pivot element has 12
• Fix the pivot and start searching form left ->
right if element is greater than pivot then it is
left position (left>pivot)
• Fix the pivot and start searching form right ->
left if element is smaller than pivot then it is
right position (right<pivot)
• 4>12 is false
21>12 is true
Then fix left position as 21
• 8<12 is true
Then fix right position has 8
12 4 21 30 25 17 5 8
Pivot left right
• Now swap left and right position
✓ After swapping :
12 4 8 30 25 17 5 21
Pivot left right
• 8>12 is false
30>12 is true
Now fix 30 as left position
• 21<12 is false
5<12 is true
Now fix 5 as right position
12 4 8 30 25 17 5 21
Pivot left right
✓ After swapping :
12 4 8 5 25 17 30 21
Pivot left right
• 5>12 is false
25>12 is true
Now fix 25 as left position
• 30<12 is false
17<12 is false
25<12 is false
5<12 is true
Now fix 5 has right position
12 4 8 5 25 17 30 21
Pivot right left
❖Note : if right comes first the swap right
element with pivot
5 4 8 12 25 17 30 21
Partition-1 pivot partition-2
• Now separate the partition 1 and 2 and again
start the quick sorting for each side
• Taking partition-1:
5 4 8
Pivot
• 4>5 is false
8>5 is true
Now take 8 has left position
• 8<5 Is false
4<5 is true
Now take 4 has right position
• According to note point swap the pivot with right
position element
✓ After swapping :
4 5 8
Pivot
• The partition-1 is arranged in order because the
elements before and after pivot element is one
• Partition-2 :
25 17 30 21
Pivot left right
• 17>25 is false
30>25 is true
Now take 30 as left position
• 21<25 is true
Now take 21 as right position
• Then swap left and right position
• Now the partition-2 becomes
25 17 21 30
Pivot left right
• 21>25 is false
30>25 is true
Now take 30 as left position
• 30<25 is false
21<25 is true
Now take 21 has right position
25 17 21 30
Pivot right left
• Now swap the right posititon and pivot
21 17 25 30
Partition-1.1 Pivot
• Partition-1.1 :
21 17
Pivot
• 17>21 is false
• 17<21 is true
Now take 17 has right position
• Now swap 17 and 21
✓ After swapping :
17 21 25 30
• Now combining partition-1 and partition-2 and
pivot the elements becomes
4 5 8 12 17 21 25 30
• Is the final sorted elements using quicksort
➢ Algorithm for quick sort : (‘i’ indicated right position
and ‘j’ indicated the left position )
• Step 1: Initialize two pointers i and j at the
beginning and end of the array (excluding the
pivot).
• Step 2: Increment i until you find an element
greater than or equal to the pivot.
• Step 3: Decrement j until you find an element
less than or equal to the pivot.
• Step 4: If i is less than j swap the elements at i
and j.
• Step 5: Repeat steps 2-4 until i and j cross each
other.
• Step 6: Swap the pivot element with the
element at j. This places the pivot in its final
position, dividing the array into two partitioned
subarrays.
• Step 7: Apply Quick Sort to the sub-arrays
(Continue recursion until each sub-array
becomes sorted)
Strassen’s Matrix Multiplication
Strassen’s Matrix Multiplication is an efficient algorithm for multiplying two
matrices. The Strassen’s method of matrix multiplication is a typical divide and
conquer algorithm. It approach to solve the matrix multiplication problems.
The usual matrix multiplication method multiplies each row with each
column to achieve the product matrix.
The time complexity taken by his approach is O(n3).
Strassen’s method was introduced to reduce the time complexity from
O(n3) to O(nlog7).
Basic method multiplication or Naive method
First, we will discuss Naive method and its complexity. Here, we are calculating
Z=𝑿X × Y. Using Naive method, two matrices (X and Y) can be multiplied if the
order of these matrices are p × q and q × r and the resultant matrix will be of
order p × r. The following pseudocode describes the Naive multiplication
−Algorithm:
Matrix-Multiplication (X, Y, Z)
for i = 1 to p do
for j = 1 to r do
Z[i,j] := 0
for k = 1 to q do
Z[i,j] := Z[i,j] + X[i,k] × Y[k,j]
1
Divide and Conquer :
Following is simple Divide and Conquer method to multiply
two square matrices.
[Link] matrices A and B in 4 sub-matrices of size N/2 x
N/2 as shown in the below diagram.
[Link] following values recursively. ae + bg, af + bh,
ce + dg and cf + dh.
Strassen’s Matrix Multiplication Algorithm
In this context, using Strassen’s Matrix multiplication
algorithm, the time consumption can be improved a little bit.
Strassen’s Matrix multiplication can be performed only
on square matrices where n is a power of 2. Order of both of
the matrices are n × n.
Divide X, Y and Z into four (n/2)×(n/2) matrices as represented
below −
Z = [I J K L ] X =[A B C D] and Y = [E F G H]
2
Using Strassen’s Algorithm compute the following −
Then,
I:=M2+M3−M6−M7
J:=M4+M6
K:=M5+M7
L:=M1−M3−M4−M5
Analysis
T(n)={c if n=1
7xT(n2)+dxn2 otherwise where c and d are
constants
Using this recurrence relation, we get T(n)=O(nlog7)
3
Hence, the complexity of Strassen’s matrix multiplication
algorithm is O(nlog7).
Strasses Algorithm
void matmul(int*A ,int*B ,int *R ,int n )
{
if(n == 1)
{
(*R)+= (*A)*(*B);
}
else
{
matmul(A,B,R,n/4);
matmul(A,B+(n/4),R+(n/4), n/4);
matmul(A+2*(n/4),B,R+2*(n/4),n/4);
matmul(A+2*(n/4,)B+(n/4) ,R+3*(n/4) ,n/4);
matmul(A+(n/4,)B+2*(n/4) ,R ,n/4);
matmul(A+(n/4,)B+3*(n/4) ,R+(n/4) ,n/4);
matmul(A+3*(n/4,)B+2*(n/4) ,R+2*(n/4) ,n/4);
matmul(A+3*(n/4,)B+3*(n/4) ,R+3*(n/4) ,n/4);
}
}
Divide matrices in sub-matrices and recursively multiply sub -matrices
4
Time Analysis
T(1) = 1 (assume N = 2k)
T(N) = 7T(N/2)
T(N) = 7kT(N/2k) = 7k
T(N) = 7logN = Nlog7= N2.81
Example:
#include<stdio.h>
int matmul(A+2*(n/4,)B+(n/4) ,R+3*(n/4) ,n/4); main(){
int z[2][2];
int i, j;
int m1, m2, m3, m4 , m5, m6, m7;
int x[2][2] = { {12, 34}, {22, 10} };
int y[2][2] = { {3, 4}, {2, 1} };
printf("The first matrix is: ");
for(i = 0; i < 2; i++)
{
printf("\n");
for(j = 0; j < 2; j++)
printf("%d\t", x[i][j]);
}
printf("\nThe second matrix is: ");
for(i = 0; i < 2; i++)
{
printf("\n");
5
for(j = 0; j < 2; j++)
printf("%d\t", y[i][j]);
}
m1= (x[0][0] + x[1][1]) * (y[0][0] + y[1][1]);
m2= (x[1][0] + x[1][1]) * y[0][0];
m3= x[0][0] * (y[0][1] - y[1][1]);
m4= x[1][1] * (y[1][0] – y[0][0]);
m5= (x[0][0] + x[0][1]) * y[1][1];
m6= (x[1][0] - x[0][0]) * (y[0][0]+y[0][1]);
m7= (x[0][1] - x[1][1]) * (y[1][0]+y[1][1]);
z[0][0] = m1 + m4- m5 + m7;
z[0][1] = m3 + m5;
z[1][0] = m2 + m4;
z[1][1] = m1 - m2 + m3 + m6;
printf("\nProduct achieved using Strassen's algorithm: ");
for(i = 0; i < 2 ; i++)
{
printf("\n");
for(j = 0; j < 2; j++)
printf("%d\t", z[i][j]);
}
return 0;
}
Output:
The first matrix is:
12 34
6
22 10
The second matrix is:
3 4
2 1
Product achieved using Strassen's algorithm:
104 82
86 98
Applications :
• Network theory
• Solution of linear system of equations
• Transformation of co-ordinate systems
• Population