DSA-Module 4 Notes
DSA-Module 4 Notes
1. Graph Algorithms
Bellman-Ford Algorithm
Definition: The Bellman-Ford algorithm is a graph algorithm that computes the shortest
paths from a single source vertex to all other vertices in a weighted graph. It can handle
graphs with negative weight edges.
Algorithm Steps:
1. Initialize the distance to the source vertex as 0 and all other vertices as infinity.
2. For each vertex, relax all edges. This means for each edge ( (u, v) ) with weight ( w ), if
( distance[u] + w < distance[v] ), update ( distance[v] ).
3. Repeat the relaxation process for ( V-1 ) times, where ( V ) is the number of vertices.
4. Check for negative weight cycles by performing one more relaxation. If any distance
can still be updated, a negative cycle exists.
Complexity:
Time Complexity: (O(V \cdot E)), where (V) is the number of vertices and (E) is the
number of edges.
Definition: In a Directed Acyclic Graph (DAG), the shortest paths from a single source can be
found using topological sorting.
Algorithm Steps:
2. Initialize the distance to the source vertex as 0 and all other vertices as infinity.
Complexity:
Definition: Johnson’s algorithm finds the shortest paths between all pairs of vertices in a
sparse graph, even with negative weights, but without negative cycles.
Algorithm Steps:
1. Add a new vertex ( q ) connected to all other vertices with zero-weight edges.
2. Use the Bellman-Ford algorithm to find the shortest paths from ( q ) to all other
vertices. If a negative cycle is detected, terminate.
3. Reweight the edges using the formula: ( w'(u, v) = w(u, v) + h[u] - h[v] ), where ( h ) is
the distance from ( q ).
4. Use Dijkstra’s algorithm for each vertex to find the shortest paths in the reweighted
graph.
Complexity:
Definition: The Ford-Fulkerson method computes the maximum flow in a flow network using
augmenting paths.
Algorithm Steps:
2. While there exists an augmenting path from the source to the sink, increase the flow
along this path.
Complexity:
Time Complexity: (O(E \cdot f)), where (f) is the maximum flow.
Definition: Maximum bipartite matching finds the largest matching in a bipartite graph.
Algorithm Steps:
Complexity:
Representation of Polynomials
Definition: A polynomial can be represented as: [ P(x) = a_0 + a_1 x + a_2 x^2 + \ldots + a_n
x^n ] where ( a_i ) are the coefÏcients.
Storage: Polynomials can be stored in an array where the index represents the power of ( x ).
Discrete Fourier Transform (DFT): The DFT transforms a sequence of complex numbers into
another sequence of complex numbers, providing frequency domain representation.
Fast Fourier Transform (FFT): The FFT is an efÏcient algorithm to compute the DFT, reducing
the time complexity from (O(N^2)) to (O(N \log N)).
Algorithm Steps:
Implementation:
Complexity:
Bellman-Ford Algorithm
Introduction
The Bellman-Ford algorithm is a fundamental algorithm in graph theory used to find the
shortest paths from a single source vertex to all other vertices in a weighted graph. It is
particularly useful for graphs that may contain negative weight edges, making it a versatile
choice for various applications in computer science, such as network routing and
optimization problems.
Definition
The Bellman-Ford algorithm computes the shortest path from a source vertex (s) to all other
vertices in a graph (G = (V, E)), where (V) is the set of vertices and (E) is the set of edges. The
algorithm can handle graphs with negative weight edges but cannot handle graphs with
negative weight cycles.
Algorithm Steps
1. Initialization:
Set the distance to all other vertices to infinity: [ distance[v] = \infty \quad \
text{for all } v \in V, v \neq s ]
2. Relaxation:
For each vertex (V-1) times, iterate through all edges ( (u, v) ) with weight
( w ):
After (V-1) iterations, perform one more iteration over all edges. If any
distance can still be updated, a negative weight cycle exists in the graph.
Pseudocode:
Complexity Analysis
Time Complexity:
The algorithm runs in (O(V \cdot E)), where (V) is the number of vertices and
(E) is the number of edges. This is because we perform relaxation for each
edge (E) for (V-1) iterations.
Space Complexity:
Example
( (A, B, 1) )
( (A, C, 4) )
( (B, C, -3) )
( (B, D, 2) )
( (C, D, 3) )
1. Initialize distances:
( distance[A] = 0 )
( distance[B] = \infty )
( distance[C] = \infty )
( distance[D] = \infty )
Update ( distance[B] = 1 )
Update ( distance[C] = 4 )
4. Final distances:
( distance[A] = 0 )
( distance[B] = 1 )
( distance[C] = -2 )
( distance[D] = 1 )
Conclusion
The Bellman-Ford algorithm is a powerful tool for finding the shortest paths in graphs with
negative weights. Its ability to detect negative weight cycles makes it a crucial algorithm in
various applications, including network routing and optimization problems. Understanding
the Bellman-Ford algorithm is essential for solving complex problems in graph theory and
computer science.
In graph theory, a Directed Acyclic Graph (DAG) is a directed graph that contains no cycles.
This property allows for efÏcient algorithms to find the shortest paths from a single source
vertex to all other vertices. The absence of cycles means that we can perform a topological
sort on the graph, which is a key step in finding the shortest paths.
Definition
The Single Source Shortest Path problem in a DAG involves finding the shortest paths from a
given source vertex (s) to all other vertices in the graph. The weights of the edges can be
positive, negative, or zero, but there should be no negative weight cycles.
Algorithm Steps
1. Topological Sorting:
Perform a topological sort of the DAG. This will give an ordering of the
vertices such that for every directed edge ( (u, v) ), vertex (u) comes before
vertex (v) in the ordering.
2. Initialization:
Set the distance to all other vertices as infinity: [ distance[v] = \infty \quad \
text{for all } v \in V, v \neq s ]
3. Relaxation:
Process each vertex in topological order. For each vertex (u), update the
distances of its adjacent vertices (v) using the relaxation technique:
Pseudocode:
Complexity Analysis
Time Complexity:
The time complexity for topological sorting is (O(V + E)), where (V) is the
number of vertices and (E) is the number of edges.
Space Complexity:
The space complexity is (O(V)) for storing the distance values and the
topological order.
Example
Vertices: (A, B, C, D, E)
(A \rightarrow B) (weight 1)
(A \rightarrow C) (weight 4)
(B \rightarrow D) (weight 2)
(C \rightarrow D) (weight 3)
(C \rightarrow E) (weight 2)
1. Topological Sort:
2. Initialization:
(distance[A] = 0)
(distance[B] = \infty)
(distance[C] = \infty)
(distance[D] = \infty)
(distance[E] = \infty)
3. Relaxation:
Process (A):
Update (distance[B] = 1)
Update (distance[C] = 4)
Process (B):
Process (C):
4. Final Distances:
(distance[A] = 0)
(distance[B] = 1)
(distance[C] = -2)
(distance[D] = 3)
(distance[E] = 6)
Conclusion
The Single Source Shortest Path algorithm for Directed Acyclic Graphs (DAGs) is efÏcient and
effective due to the properties of DAGs that allow for topological sorting. This algorithm is
widely used in various applications, including scheduling, optimization, and network routing.
Understanding this algorithm is essential for solving shortest path problems in directed
graphs.
Johnson’s Algorithm is an efÏcient method for finding the shortest paths between all pairs of
vertices in a weighted directed graph. It is particularly useful for sparse graphs, where the
number of edges is much less than the square of the number of vertices. The algorithm can
handle graphs with negative weights but does not allow negative weight cycles.
Overview
Algorithm Steps
Introduce a new vertex (q) and connect it to all other vertices in the graph
with edges of weight 0. This allows the Bellman-Ford algorithm to compute
the shortest paths from (q) to all other vertices.
Use the Bellman-Ford algorithm starting from the new vertex (q) to compute
the shortest path distances (h[v]) from (q) to each vertex (v). If a negative
weight cycle is detected, terminate the algorithm.
For each edge ( (u, v) ) with weight ( w(u, v) ), reweight the edges using the
formula: [ w'(u, v) = w(u, v) + h[u] - h[v] ]
This transformation ensures that all edge weights (w'(u, v)) are non-negative.
For each vertex (v) in the original graph, run Dijkstra’s algorithm using the
reweighted edges (w'(u, v)) to find the shortest paths from (v) to all other
vertices.
After obtaining the shortest path distances from Dijkstra’s algorithm, adjust
the distances back to the original weights using: [ distance[v] = d[v] -
h[source] + h[v] ]
Pseudocode
Complexity Analysis
Time Complexity:
Dijkstra’s algorithm runs in (O((V + E) \log V)) when using a priority queue.
Therefore, the overall time complexity of Johnson’s algorithm is: [ O(V \cdot E
+ V^2 \log V) ]
This is efÏcient for sparse graphs where (E) is much less than (V^2).
Space Complexity:
The space complexity is (O(V)) for storing distances and (O(V + E)) for the
graph representation.
Example
(A \rightarrow B) (weight 1)
(A \rightarrow C) (weight 4)
(B \rightarrow D) (weight 2)
(C \rightarrow D) (weight 3)
2. Run Bellman-Ford:
3. Reweight Edges:
For each vertex, compute the shortest paths using the reweighted edges.
5. Adjust Distances:
Conclusion
Johnson’s Algorithm is a powerful method for finding the shortest paths between all pairs of
vertices in sparse directed graphs. By combining the Bellman-Ford and Dijkstra’s algorithms,
it efÏciently handles graphs with negative weights while ensuring that the results are
accurate. Understanding Johnson’s Algorithm is essential for solving complex shortest path
problems in various applications, including network routing and optimization.
Flow networks are directed graphs where each edge has a capacity and each edge receives a
flow. The flow must satisfy certain constraints, and the goal is often to find the maximum
flow from a source vertex to a sink vertex. The Ford-Fulkerson method is a popular algorithm
used to compute the maximum flow in a flow network.
A flow network is defined as a directed graph ( G = (V, E) ) with the following components:
Edges: A set of directed edges ( (u, v) ) with a capacity ( c(u, v) ) that represents the
maximum flow that can pass through the edge.
Flow: A function ( f(u, v) ) that represents the flow from vertex ( u ) to vertex ( v ),
which must satisfy:
Ford-Fulkerson Method
The Ford-Fulkerson method computes the maximum flow in a flow network using the
concept of augmenting paths.
Algorithm Steps
1. Initialization:
While there exists an augmenting path from the source ( s ) to the sink ( t ) in
the residual graph, continue to augment the flow. The residual graph is
constructed by considering the remaining capacities of the edges after
accounting for the current flow.
3. Augment Flow:
For each augmenting path found, determine the minimum capacity along the
path, known as the bottleneck capacity.
Increase the flow along the path by the bottleneck capacity and update the
residual capacities of the edges accordingly.
4. Repeat:
5. Result:
Pseudocode
Complexity Analysis
Time Complexity:
If DFS is used, the complexity can be (O(E \cdot f)), where (f) is the maximum
flow.
Space Complexity:
The space complexity is (O(V + E)) for storing the graph and the flow values.
Example
Vertices: (s, a, b, t)
(s \rightarrow b) (capacity 5)
1. Initialization:
3. Augment Flow:
For the path (s \rightarrow a \rightarrow t), the bottleneck is 10. Update the
flow accordingly.
4. Repeat:
Continue finding paths and augmenting flow until no more augmenting paths
exist.
5. Result:
Conclusion
The Ford-Fulkerson method is a fundamental algorithm for solving the maximum flow
problem in flow networks. Its ability to handle various edge capacities and its
straightforward implementation make it a valuable tool in network flow analysis.
Understanding this method is essential for solving complex problems in operations research,
network design, and optimization.
Bipartite graphs are a special type of graph where the set of vertices can be divided into two
disjoint sets such that no two graph vertices within the same set are adjacent.
The Maximum Bipartite Matching problem involves finding the largest matching in a
bipartite graph, which is a set of edges that pairs vertices from the two sets without any
shared vertices.
Definition
A matching in a bipartite graph ( G = (U, V, E) ) is a subset of edges such that no two edges
share a vertex. The goal of the maximum bipartite matching problem is to find the largest
possible matching, which maximizes the number of edges in the matching.
Applications
Job Assignments: Assigning jobs to workers where each worker can perform certain
jobs.
One of the most common algorithms to find the maximum bipartite matching is
the Hopcroft-Karp algorithm, which uses a combination of breadth-first search (BFS) and
depth-first search (DFS).
Algorithm Steps
1. Initialization:
Use BFS to find the shortest augmenting path in the residual graph. An
augmenting path is a path that starts from an unmatched vertex in set ( U )
and ends at an unmatched vertex in set ( V ).
For each vertex found in the BFS, use DFS to find and augment the matching
along the path.
4. Repeat:
Repeat the BFS and DFS steps until no more augmenting paths can be found.
5. Result:
The matching set ( M ) will contain the maximum matching after the
algorithm terminates.
Pseudocode
Complexity Analysis
Time Complexity:
The Hopcroft-Karp algorithm runs in (O(E \sqrt{V})), where (E) is the number
of edges and (V) is the number of vertices in the bipartite graph.
Space Complexity:
The space complexity is (O(V + E)) for storing the graph and the matching.
Example
Set ( U = {A, B, C} )
Set ( V = {1, 2, 3} )
Edges:
( A \rightarrow 1 )
( A \rightarrow 2 )
( B \rightarrow 2 )
( C \rightarrow 3 )
1. Initialization:
2. BFS:
3. DFS:
4. Repeat:
5. Result:
The maximum matching might be ( M = {(A, 1), (B, 2), (C, 3)} ).
Conclusion
1. Representation of Polynomials
Definition: A polynomial ( P(x) ) of degree ( n ) can be represented as: [ P(x) = a_0 + a_1 x +
a_2 x^2 + \ldots + a_n x^n ] where ( a_0, a_1, \ldots, a_n ) are the coefÏcients of the
polynomial.
Storage: Polynomials can be stored in an array where the index represents the power of ( x ):
Definition: The Discrete Fourier Transform (DFT) transforms a sequence of complex numbers
into another sequence of complex numbers, providing a frequency domain representation.
Formula: For a sequence ( x_0, x_1, \ldots, x_{N-1} ), the DFT is defined as: [ X(k) = \
sum_{n=0}^{N-1} x(n) e^{-2\pi i kn/N} \quad \text{for } k = 0, 1, \ldots, N-1 ] where ( X(k) ) is
the k-th frequency component.
Definition: The Fast Fourier Transform (FFT) is an efÏcient algorithm to compute the DFT,
reducing the time complexity from ( O(N^2) ) to ( O(N \log N) ).
Algorithm Steps:
Split the input sequence into two halves: one containing the even-indexed
elements and the other containing the odd-indexed elements.
2. Combine:
Use the results from the two halves to compute the DFT of the original
sequence using the properties of roots of unity.
Cooley-Tukey Algorithm: The most common FFT algorithm is the Cooley-Tukey algorithm,
which recursively breaks down the DFT into smaller DFTs.
Implementation:
1. Recursive Approach:
2. In-Place Computation:
Use in-place computation to save space and avoid creating additional arrays.
3. Bit-Reversal Permutation:
Complexity:
5. Applications of FFT
Signal Processing: FFT is widely used in digital signal processing for analyzing
frequencies in signals.
1. Polynomials:
4. Inverse FFT:
Use the inverse FFT to convert the results back to the coefÏcient form of the
resulting polynomial.
Conclusion
Polynomials and the Fast Fourier Transform (FFT) are fundamental concepts in computer
science and mathematics. The FFT provides an efÏcient method for performing operations
on polynomials, such as multiplication, in a time-efÏcient manner. Understanding these
concepts is crucial for solving problems in various fields, including signal processing, image
analysis, and numerical computations.
1. Representation of Polynomials
Storage: Polynomials can be represented in various ways, but the most common method is
using an array:
The coefÏcients of the polynomial are stored in an array where the index represents
the power of ( x ).
Formula: For a sequence ( x_0, x_1, \ldots, x_{N-1} ), the DFT is defined as: [ X(k) = \
sum_{n=0}^{N-1} x(n) e^{-2\pi i kn/N} \quad \text{for } k = 0, 1, \ldots, N-1 ] where ( X(k) ) is
the k-th frequency component.
Properties:
The DFT converts time-domain signals into frequency-domain signals, allowing for
analysis of the frequency components.
Definition: The Fast Fourier Transform (FFT) is an efÏcient algorithm to compute the DFT,
reducing the time complexity from ( O(N^2) ) to ( O(N \log N) ). The FFT is widely used in
various applications, including signal processing, image analysis, and polynomial
multiplication.
Algorithm Steps:
Split the input sequence into two halves: one containing the even-indexed
elements and the other containing the odd-indexed elements.
2. Combine:
Use the results from the two halves to compute the DFT of the original
sequence using the properties of roots of unity.
Cooley-Tukey Algorithm: The most common FFT algorithm is the Cooley-Tukey algorithm,
which recursively breaks down the DFT into smaller DFTs.
Implementation:
1. Recursive Approach:
The basic idea is to recursively compute the FFT for the even and odd indexed
elements.
2. In-Place Computation:
Use in-place computation to save space and avoid creating additional arrays.
This can be achieved by reordering the input array using bit-reversal.
3. Bit-Reversal Permutation:
Reorder the input array using bit-reversal to improve cache performance and
reduce memory access time.
function FFT(x):
N = length(x)
if N <= 1:
return x
return [even[k] + T[k] for k in range(N // 2)] + [even[k] - T[k] for k in range(N // 2)]
Complexity Analysis
Time Complexity: The FFT algorithm runs in ( O(N \log N) ), making it significantly
faster than the naive DFT computation.
Space Complexity: The space complexity is ( O(N) ) for storing intermediate results.
Applications of FFT
Signal Processing: FFT is widely used in digital signal processing for analyzing
frequencies in signals.
Conclusion
The representation of polynomials, the Discrete Fourier Transform (DFT), and the Fast
Fourier Transform (FFT) are fundamental concepts in computer science and mathematics.
The FFT provides an efÏcient method for performing operations on polynomials and
analyzing signals, making it a crucial tool in various applications. Understanding these
concepts is essential for solving problems in numerical computations, signal processing, and
data analysis.