0% found this document useful (0 votes)
11 views34 pages

GRAPH C

The document provides an overview of Depth-First Search (DFS) and Breadth-First Search (BFS) algorithms, detailing their theories, algorithms, time and space complexities, and use cases. It includes C++ implementations for both algorithms using adjacency lists and matrices, highlighting their performance differences. The summary also compares DFS and BFS in terms of traversal type, memory usage, and best use cases for different graph types.

Uploaded by

Abaid Anwer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views34 pages

GRAPH C

The document provides an overview of Depth-First Search (DFS) and Breadth-First Search (BFS) algorithms, detailing their theories, algorithms, time and space complexities, and use cases. It includes C++ implementations for both algorithms using adjacency lists and matrices, highlighting their performance differences. The summary also compares DFS and BFS in terms of traversal type, memory usage, and best use cases for different graph types.

Uploaded by

Abaid Anwer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 34

GRAPH c++

Depth-First Search (DFS) and Breadth-First Search (BFS) in Graphs


Both DFS and BFS are fundamental graph traversal algorithms that explore all the vertices and edges of a
graph in a systematic manner. They are used in many applications such as finding paths, exploring nodes,
and solving problems like the shortest path in an unweighted graph, detecting cycles, etc.
Depth-First Search (DFS)
Theory: DFS explores as far as possible along each branch before backtracking. Starting from a source
node, DFS goes deep into the graph by visiting an unvisited adjacent node, then recursively visits the next
unvisited adjacent node, and so on. If a node has no unvisited adjacent nodes, the algorithm backtracks to
the previous node and continues the search. This is typically implemented using recursion or an explicit
stack.
DFS Algorithm Steps:
1. Start from the root node (or any arbitrary node).
2. Visit the node and mark it as visited.
3. Recursively visit all adjacent (unvisited) nodes.
4. When there are no adjacent unvisited nodes left, backtrack to the last visited node with unvisited
neighbors.
5. Repeat this process until all nodes are visited.
Time Complexity:
 Time complexity: O(V + E), where:
o V = number of vertices

o E = number of edges

 This is because in DFS, each vertex is visited once, and every edge is explored once.
Space Complexity:
 Space complexity: O(V) in the worst case, because we store the visited nodes and the recursion
stack (or explicit stack) can hold up to V elements.
Use Cases:
 Pathfinding, cycle detection, topological sorting, solving mazes, etc.
Example of DFS in C++:
cpp
Copy code
#include <iostream>
#include <vector>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjList;

Graph(int V) {
this->V = V;
adjList.resize(V);
}

void addEdge(int u, int v) {


adjList[u].push_back(v);
}

void DFS(int v, vector<bool>& visited) {


visited[v] = true;
cout << v << " ";

for (int u : adjList[v]) {


if (!visited[u]) {
DFS(u, visited);
}
}
}
};
int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

vector<bool> visited(6, false);


cout << "DFS starting from node 0:" << endl;
g.DFS(0, visited); // Output: 0 1 3 4 2 5
return 0;
}
Breadth-First Search (BFS)
Theory: BFS explores all the vertices at the present depth level before moving on to vertices at the next
depth level. It uses a queue to explore the graph level by level. BFS starts at a given node, then explores
all its neighbors before moving to their unvisited neighbors. This ensures that nodes closer to the start are
visited first.
BFS Algorithm Steps:
1. Start from the root node (or any arbitrary node).
2. Visit the node and mark it as visited.
3. Add all unvisited adjacent nodes to a queue.
4. Dequeue a node, visit it, and add its unvisited adjacent nodes to the queue.
5. Repeat the process until the queue is empty.
Time Complexity:
 Time complexity: O(V + E), where:
o V = number of vertices

o E = number of edges

 Each vertex is processed once, and each edge is explored once.


Space Complexity:
 Space complexity: O(V), because we store the visited nodes and the queue can hold up to V
elements.
Use Cases:
 Shortest path in an unweighted graph, finding all nodes at a specific distance from a source node,
solving puzzles like the "8-puzzle," etc.
Example of BFS in C++:
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjList;

Graph(int V) {
this->V = V;
adjList.resize(V);
}

void addEdge(int u, int v) {


adjList[u].push_back(v);
}

void BFS(int start) {


vector<bool> visited(V, false);
queue<int> q;
visited[start] = true;
q.push(start);

while (!q.empty()) {
int v = q.front();
cout << v << " ";
q.pop();

for (int u : adjList[v]) {


if (!visited[u]) {
visited[u] = true;
q.push(u);
}
}
}
}
};

int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

cout << "BFS starting from node 0:" << endl;


g.BFS(0); // Output: 0 1 2 3 4 5
return 0;
}
Comparison between DFS and BFS:

Aspect DFS BFS

Traversal Type Deep, recursive (or stack-based) Level-wise, queue-based

Memory Usage O(V) due to recursion stack O(V) due to queue storage

Time
O(V + E) O(V + E)
Complexity

Space
O(V) O(V)
Complexity

Pathfinding in deep graphs, cycle Finding the shortest path in an unweighted


Best for
detection, topological sorting graph, level-order traversal

Can be used for both directed and


Graph Type Typically used for unweighted graphs
undirected graphs

Edge Explores all edges at the current level before


Explores a path before backtracking
Exploration moving deeper

Summary:
 DFS explores deeply into a graph, utilizing a stack or recursion, and is suitable for problems like
topological sorting and cycle detection.
 BFS explores level by level and is ideal for problems like finding the shortest path in unweighted
graphs.
Both algorithms have the same time and space complexity, but their application differs based on the
problem's needs (e.g., DFS for deep searches and BFS for shallow searches).
4o mini
You said:
using adj list and matrix
ChatGPT said:
ChatGPT
Sure! Let's explore both DFS and BFS using adjacency matrix and adjacency list representations for
graphs.
Adjacency Matrix Representation
In an adjacency matrix, a graph with VVV vertices is represented as a 2D matrix of size V×VV \times
VV×V, where each element adjMatrix[i][j]adjMatrix[i][j]adjMatrix[i][j] indicates the presence (or
absence) of an edge between vertices iii and jjj. If there is an edge, adjMatrix[i][j]=1adjMatrix[i][j] =
1adjMatrix[i][j]=1, otherwise adjMatrix[i][j]=0adjMatrix[i][j] = 0adjMatrix[i][j]=0.
Adjacency List Representation
In an adjacency list, each vertex has a list of all adjacent vertices. This is often implemented as an array of
linked lists (or vectors), where each index corresponds to a vertex, and the list contains the vertices that it
is connected to.

DFS Using Adjacency Matrix


1. DFS with Adjacency Matrix
In DFS with an adjacency matrix, we explore the graph starting from a node and recursively visit its
neighbors. Here's the implementation:
Time Complexity: O(V2)O(V^2)O(V2) due to the adjacency matrix lookup.
cpp
Copy code
#include <iostream>
#include <vector>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjMatrix;

Graph(int V) {
this->V = V;
adjMatrix.resize(V, vector<int>(V, 0));
}

void addEdge(int u, int v) {


adjMatrix[u][v] = 1;
adjMatrix[v][u] = 1; // For undirected graph
}

void DFS(int v, vector<bool>& visited) {


visited[v] = true;
cout << v << " ";

for (int u = 0; u < V; ++u) {


if (adjMatrix[v][u] == 1 && !visited[u]) {
DFS(u, visited);
}
}
}
};

int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

vector<bool> visited(6, false);


cout << "DFS starting from node 0:" << endl;
g.DFS(0, visited); // Output: 0 1 3 4 2 5
return 0;
}

BFS Using Adjacency Matrix


2. BFS with Adjacency Matrix
In BFS with an adjacency matrix, we use a queue to explore nodes level by level. Here's the
implementation:
Time Complexity: O(V2)O(V^2)O(V2) due to the adjacency matrix lookup.
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjMatrix;

Graph(int V) {
this->V = V;
adjMatrix.resize(V, vector<int>(V, 0));
}

void addEdge(int u, int v) {


adjMatrix[u][v] = 1;
adjMatrix[v][u] = 1; // For undirected graph
}

void BFS(int start) {


vector<bool> visited(V, false);
queue<int> q;

visited[start] = true;
q.push(start);

while (!q.empty()) {
int v = q.front();
cout << v << " ";
q.pop();

for (int u = 0; u < V; ++u) {


if (adjMatrix[v][u] == 1 && !visited[u]) {
visited[u] = true;
q.push(u);
}
}
}
}
};

int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

cout << "BFS starting from node 0:" << endl;


g.BFS(0); // Output: 0 1 2 3 4 5
return 0;
}

DFS Using Adjacency List


3. DFS with Adjacency List
In DFS with an adjacency list, we recursively visit each vertex and explore its neighbors using a list.
Here's the implementation:
Time Complexity: O(V+E)O(V + E)O(V+E) because we are visiting each vertex and edge.
cpp
Copy code
#include <iostream>
#include <vector>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjList;

Graph(int V) {
this->V = V;
adjList.resize(V);
}

void addEdge(int u, int v) {


adjList[u].push_back(v);
adjList[v].push_back(u); // For undirected graph
}

void DFS(int v, vector<bool>& visited) {


visited[v] = true;
cout << v << " ";

for (int u : adjList[v]) {


if (!visited[u]) {
DFS(u, visited);
}
}
}
};

int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

vector<bool> visited(6, false);


cout << "DFS starting from node 0:" << endl;
g.DFS(0, visited); // Output: 0 1 3 4 2 5
return 0;
}

BFS Using Adjacency List


4. BFS with Adjacency List
In BFS with an adjacency list, we use a queue to explore the graph level by level, similar to BFS with an
adjacency matrix. Here's the implementation:
Time Complexity: O(V+E)O(V + E)O(V+E) because we visit each vertex and edge once.
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Graph {
public:
int V;
vector<vector<int>> adjList;

Graph(int V) {
this->V = V;
adjList.resize(V);
}

void addEdge(int u, int v) {


adjList[u].push_back(v);
adjList[v].push_back(u); // For undirected graph
}

void BFS(int start) {


vector<bool> visited(V, false);
queue<int> q;

visited[start] = true;
q.push(start);

while (!q.empty()) {
int v = q.front();
cout << v << " ";
q.pop();

for (int u : adjList[v]) {


if (!visited[u]) {
visited[u] = true;
q.push(u);
}
}
}
}
};

int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);

cout << "BFS starting from node 0:" << endl;


g.BFS(0); // Output: 0 1 2 3 4 5
return 0;
}

Comparison:

Representation DFS BFS

Adjacency Matrix O(V2)O(V^2)O(V2) O(V2)O(V^2)O(V2)

Adjacency List O(V+E)O(V + E)O(V+E) O(V+E)O(V + E)O(V+E)

Space Complexity O(V2)O(V^2)O(V2) (Matrix) O(V+E)O(V + E)O(V+E) (List)

Summary:
 Adjacency Matrix: Suitable for dense graphs. It has a space complexity of O(V2)O(V^2)O(V2),
and both DFS and BFS have a time complexity of O(V2)O(V^2)O(V2) because we have to check
each possible edge.
 Adjacency List: Suitable for sparse graphs. The time complexity for both DFS and BFS is
O(V+E)O(V + E)O(V+E), where EEE is the number of edges.
Both representations have their use cases depending on the nature of the graph and the specific problem
you're working on
Depth-First Search (DFS) in Graphs
Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch
before backtracking. It can be implemented using either adjacency list or adjacency matrix
representations of a graph.
Here’s a complete explanation with code for both representations:

1. DFS using Adjacency List


An adjacency list is an efficient way to represent sparse graphs. Each vertex has a list of adjacent vertices.
cpp
Copy code
#include <iostream>
#include <vector>
using namespace std;

void dfsList(int node, vector<bool>& visited, const vector<vector<int>>& adjList) {


// Mark the current node as visited
visited[node] = true;
cout << node << " ";

// Visit all unvisited neighbors


for (int neighbor : adjList[node]) {
if (!visited[neighbor]) {
dfsList(neighbor, visited, adjList);
}
}
}

int main() {
// Number of nodes and edges
int nodes = 6;
vector<vector<int>> adjList(nodes);

// Edges (undirected graph)


adjList[0] = {1, 2}; // Node 0 is connected to 1 and 2
adjList[1] = {0, 3}; // Node 1 is connected to 0 and 3
adjList[2] = {0, 4}; // Node 2 is connected to 0 and 4
adjList[3] = {1, 5}; // Node 3 is connected to 1 and 5
adjList[4] = {2}; // Node 4 is connected to 2
adjList[5] = {3}; // Node 5 is connected to 3

vector<bool> visited(nodes, false);

// Start DFS from node 0


cout << "DFS Traversal (Adjacency List): ";
dfsList(0, visited, adjList);

return 0;
}

2. DFS using Adjacency Matrix


An adjacency matrix is a 2D array where matrix[i][j] indicates whether there is an edge between nodes i
and j.
cpp
Copy code
#include <iostream>
#include <vector>
using namespace std;

void dfsMatrix(int node, vector<bool>& visited, const vector<vector<int>>& adjMatrix) {


// Mark the current node as visited
visited[node] = true;
cout << node << " ";

// Visit all unvisited neighbors


for (int i = 0; i < adjMatrix.size(); i++) {
if (adjMatrix[node][i] == 1 && !visited[i]) {
dfsMatrix(i, visited, adjMatrix);
}
}
}

int main() {
// Number of nodes
int nodes = 6;
vector<vector<int>> adjMatrix(nodes, vector<int>(nodes, 0));

// Edges (undirected graph)


adjMatrix[0][1] = adjMatrix[1][0] = 1;
adjMatrix[0][2] = adjMatrix[2][0] = 1;
adjMatrix[1][3] = adjMatrix[3][1] = 1;
adjMatrix[2][4] = adjMatrix[4][2] = 1;
adjMatrix[3][5] = adjMatrix[5][3] = 1;

vector<bool> visited(nodes, false);


// Start DFS from node 0
cout << "DFS Traversal (Adjacency Matrix): ";
dfsMatrix(0, visited, adjMatrix);

return 0;
}

Key Points in Both Approaches


1. Base Logic:
o Mark the node as visited.

o Visit all unvisited neighbors recursively.

2. Input Graph:
o In the adjacency list, edges are stored as lists for each node.

o In the adjacency matrix, edges are stored in a 2D matrix.

3. Time Complexity:
o Adjacency List: O(V+E)O(V + E)O(V+E), where VVV is the number of vertices and
EEE is the number of edges.
o Adjacency Matrix: O(V2)O(V^2)O(V2), since we check all pairs of nodes.

4. Space Complexity:
o Adjacency List: O(V+E)O(V + E)O(V+E), for storing the edges.

o Adjacency Matrix: O(V2)O(V^2)O(V2), for the 2D matrix.

Output Example
For the above input graphs, the DFS traversal starting from node 0 will produce:
java
Copy code
DFS Traversal (Adjacency List): 0 1 3 5 2 4
DFS Traversal (Adjacency Matrix): 0 1 3 5 2 4
This demonstrates that DFS visits all nodes reachable from the starting node by exploring each branch to
its depth.
4o
You said:
now BFS
ChatGPT said:
ChatGPT
Breadth-First Search (BFS) in Graphs
Breadth-First Search (BFS) is a graph traversal algorithm that explores all neighbors at the current depth
before moving to nodes at the next depth level. BFS uses a queue data structure to ensure nodes are
visited in level order.
Here’s a complete explanation with BFS code for adjacency list and adjacency matrix representations.

1. BFS using Adjacency List


An adjacency list is a space-efficient representation for graphs. Each node maintains a list of its
neighbors.
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

void bfsList(int startNode, const vector<vector<int>>& adjList) {


int nodes = adjList.size();
vector<bool> visited(nodes, false);
queue<int> q;

// Start BFS
q.push(startNode);
visited[startNode] = true;

cout << "BFS Traversal (Adjacency List): ";


while (!q.empty()) {
int currentNode = q.front();
q.pop();
cout << currentNode << " ";

// Add unvisited neighbors to the queue


for (int neighbor : adjList[currentNode]) {
if (!visited[neighbor]) {
q.push(neighbor);
visited[neighbor] = true;
}
}
}
cout << endl;
}

int main() {
// Number of nodes and edges
int nodes = 6;
vector<vector<int>> adjList(nodes);

// Edges (undirected graph)


adjList[0] = {1, 2};
adjList[1] = {0, 3};
adjList[2] = {0, 4};
adjList[3] = {1, 5};
adjList[4] = {2};
adjList[5] = {3};

// Start BFS from node 0


bfsList(0, adjList);

return 0;
}

2. BFS using Adjacency Matrix


An adjacency matrix uses a 2D array to represent edges between nodes. It's efficient for dense graphs.
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

void bfsMatrix(int startNode, const vector<vector<int>>& adjMatrix) {


int nodes = adjMatrix.size();
vector<bool> visited(nodes, false);
queue<int> q;

// Start BFS
q.push(startNode);
visited[startNode] = true;

cout << "BFS Traversal (Adjacency Matrix): ";


while (!q.empty()) {
int currentNode = q.front();
q.pop();
cout << currentNode << " ";

// Add unvisited neighbors to the queue


for (int i = 0; i < nodes; i++) {
if (adjMatrix[currentNode][i] == 1 && !visited[i]) {
q.push(i);
visited[i] = true;
}
}
}
cout << endl;
}

int main() {
// Number of nodes
int nodes = 6;
vector<vector<int>> adjMatrix(nodes, vector<int>(nodes, 0));

// Edges (undirected graph)


adjMatrix[0][1] = adjMatrix[1][0] = 1;
adjMatrix[0][2] = adjMatrix[2][0] = 1;
adjMatrix[1][3] = adjMatrix[3][1] = 1;
adjMatrix[2][4] = adjMatrix[4][2] = 1;
adjMatrix[3][5] = adjMatrix[5][3] = 1;

// Start BFS from node 0


bfsMatrix(0, adjMatrix);

return 0;
}

Key Points in BFS


1. Algorithm Steps:
o Start at the given node.

o Mark it as visited and enqueue it.

o Dequeue a node, print it, and enqueue all its unvisited neighbors.

o Repeat until the queue is empty.

2. Input Graph:
o Adjacency List: Uses a vector of vectors to store neighbors.

o Adjacency Matrix: Uses a 2D array to indicate edges between nodes.

3. Time Complexity:
o Adjacency List: O(V+E)O(V + E)O(V+E), where VVV is the number of vertices and
EEE is the number of edges.
o Adjacency Matrix: O(V2)O(V^2)O(V2), as every pair of nodes is checked.

4. Space Complexity:
o Adjacency List: O(V+E)O(V + E)O(V+E), to store the edges.

o Adjacency Matrix: O(V2)O(V^2)O(V2), for the 2D matrix.

Output Example
For the given input graphs, BFS starting from node 0 produces:
java
Copy code
BFS Traversal (Adjacency List): 0 1 2 3 4 5
BFS Traversal (Adjacency Matrix): 0 1 2 3 4 5
This demonstrates that BFS visits nodes level by level, ensuring all nodes at the same depth are processed
before moving deeper.
4o
You said:
give me brief overview how dfs and bfs works and when to use tehm etc just explanation
ChatGPT said:
ChatGPT
Overview of DFS and BFS
Depth-First Search (DFS):
 How it works:
DFS explores a graph deeply along a branch before backtracking. It uses recursion or a stack to
keep track of the nodes to visit.
o Start at the root node.

o Mark it as visited.

o Move to one unvisited neighbor, marking it as visited.

o Continue until there are no more unvisited neighbors, then backtrack.

 Key Characteristics:
o Can be implemented recursively or iteratively (using a stack).

o Explores one path fully before moving to another.

o May not find the shortest path (if searching in an unweighted graph).

 Use Cases:
o Finding connected components in a graph.

o Detecting cycles in a graph.

o Solving maze problems (exploring all paths).

o Topological sorting for dependency resolution.

o Game or puzzle scenarios (like Sudoku or N-Queens).

Breadth-First Search (BFS):


 How it works:
BFS explores a graph level by level starting from the root node. It uses a queue to manage the
nodes to visit.
o Start at the root node.

o Enqueue it and mark it as visited.

o Dequeue the front node, explore its unvisited neighbors, and enqueue them.

o Repeat until the queue is empty.

 Key Characteristics:
o Always finds the shortest path in an unweighted graph.

o Uses a queue (FIFO) for traversal.

 Use Cases:
o Finding the shortest path in an unweighted graph.
o Solving problems like minimum moves in a game.

o Testing for bipartite graphs.

o Level-order traversal in trees.

Comparison: When to Use DFS or BFS

Aspect DFS BFS

Traversal Style Deep first, backtrack later Broad, level-by-level

Data Structure Stack (or recursion) Queue

Guarantees shortest path in unweighted


Pathfinding Does not guarantee the shortest path
graphs

O(V)O(V)O(V) in recursion (stack


Space Complexity O(V)O(V)O(V) for queue
space)

Time Complexity O(V+E)O(V + E)O(V+E) O(V+E)O(V + E)O(V+E)

Best For Exploring all paths or components Finding shortest path, level-wise tasks

Examples Cycle detection, maze-solving Shortest path, level-order traversal

Which to Choose?
 Use DFS:
o When you need to explore every path deeply.

o Problems like topological sorting, connectivity, or cycle detection.

o Space is less of a concern in smaller graphs.

 Use BFS:
o When you need the shortest path in unweighted graphs.

o Tasks requiring level-by-level processing.

o For problems like finding the minimum number of moves or layers in a network.

In summary:
 DFS is better for exploring structure and solving puzzles.
 BFS is better for shortest path or level-order traversal tasks

Topological sort c++


Kahn's Algorithm is used for topological sorting of a Directed Acyclic Graph (DAG). In
topological sorting, the vertices of the graph are arranged in a linear order such that for every
directed edge (u, v), vertex u comes before vertex v in the ordering.
Here’s a step-by-step explanation of Kahn's Algorithm and its C++ implementation:
Steps of Kahn's Algorithm:
1. Compute the in-degree of each vertex. The in-degree of a vertex is the number of edges
directed toward it.
2. Initialize a queue and enqueue all vertices with in-degree 0. These vertices have no
dependencies.
3. Process the queue:
o Dequeue a vertex from the queue and add it to the topological order.
o For each adjacent vertex of the dequeued vertex, reduce its in-degree by 1.
o If the in-degree of an adjacent vertex becomes 0, enqueue it.
4. Repeat the process until the queue is empty.
5. Check for cycles: If the topological sort contains fewer vertices than the total number of
vertices, there is a cycle in the graph.
C++ Code Implementation:
cpp
Copy code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

void topologicalSort(int V, vector<vector<int>>& adj) {


vector<int> in_degree(V, 0);

// Step 1: Calculate in-degree of each vertex


for (int u = 0; u < V; u++) {
for (int v : adj[u]) {
in_degree[v]++;
}
}

// Step 2: Initialize a queue and enqueue vertices with in-degree 0


queue<int> q;
for (int i = 0; i < V; i++) {
if (in_degree[i] == 0) {
q.push(i);
}
}

vector<int> topo_order;

// Step 3: Process the queue


while (!q.empty()) {
int u = q.front();
q.pop();

topo_order.push_back(u);

for (int v : adj[u]) {


in_degree[v]--;
if (in_degree[v] == 0) {
q.push(v);
}
}
}
// Step 4: Check if there was a cycle (if topo_order size != V)
if (topo_order.size() != V) {
cout << "Cycle detected, topological sorting is not possible" << endl;
} else {
// Print topological order
for (int i : topo_order) {
cout << i << " ";
}
cout << endl;
}
}

int main() {
int V = 6;
vector<vector<int>> adj(V);

// Example graph
adj[5].push_back(2);
adj[5].push_back(0);
adj[4].push_back(0);
adj[4].push_back(1);
adj[2].push_back(3);
adj[3].push_back(1);

cout << "Topological Sorting: ";


topologicalSort(V, adj);
return 0;
}
Explanation:
 in_degree vector: Keeps track of the number of incoming edges for each vertex.
 Queue: Stores vertices that have no incoming edges (i.e., in-degree of 0).
 topo_order vector: Holds the final topological order.
 Adjacency list: adj[u] contains all vertices that can be reached by an edge from vertex u.
Example:
For the graph represented by the adjacency list:
rust
Copy code
5 -> 2
5 -> 0
4 -> 0
4 -> 1
2 -> 3
3 -> 1
The output of the program will be:
yaml
Copy code
Topological Sorting: 5 4 2 3 1 0
This means the topological order of the graph is 5, 4, 2, 3, 1, 0, and all dependencies are
respected in the ordering

The Prerequisite Tasks Problem is a classic problem related to topological sorting.


The task is to determine if it's possible to complete all the tasks given their
prerequisites.
Problem Statement:
You are given a list of tasks and a list of prerequisite pairs. Each pair (a, b) means
that task a depends on task b, i.e., you must complete task b before you can start
task a. Your goal is to determine if it is possible to complete all tasks given the
constraints.
Input:
 An integer n, the total number of tasks.
 A list of prerequisite pairs, where each pair is a tuple (a, b) representing that
task a depends on task b.
Output:
 Return true if it is possible to finish all tasks, otherwise return false.
Example:
cpp
Copy code
n=2
prerequisites = [[1, 0]]
Output: true
Explanation: Task 1 depends on task 0, so it's possible to complete task 0 first, then
task 1.
cpp
Copy code
n=2
prerequisites = [[1, 0], [0, 1]]
Output: false
Explanation: There is a cycle (task 0 depends on task 1, and task 1 depends on task
0), making it impossible to complete the tasks.
Approach & Intuition:
1. Graph Representation:
o Treat each task as a node in a directed graph.
o A prerequisite pair (a, b) means there is a directed edge from task b to
task a.
2. Topological Sorting:
o The task is to find a valid topological order of the tasks. If there is a
cycle, topological sorting is not possible.
o We can use Kahn's Algorithm (modified BFS) or DFS-based approach
to solve this.
3. Cycle Detection:
o If we can perform a topological sort and visit all tasks, then it is
possible to finish all tasks.
o If we can't visit all tasks (because some tasks are part of a cycle), it's
impossible to finish them.
Solution using Kahn's Algorithm (BFS-based):
1. In-degree Calculation: Calculate the in-degree (number of incoming edges)
for each task. Tasks with in-degree 0 have no prerequisites and can be
processed first.
2. Queue Initialization: Initialize a queue with all tasks having in-degree 0.
3. Process Tasks:
o Dequeue a task, add it to the result (topological order), and decrease
the in-degree of its dependent tasks.
o If any dependent task's in-degree becomes 0, enqueue it.
4. Cycle Detection: If the number of tasks processed is less than n, there is a
cycle, and it's impossible to complete all tasks
#include <iostream>
#include <vector>
#include <queue> // For using the queue data structure
using namespace std;
// Function to determine if it's possible to finish all tasks
bool canFinish(int n, vector<vector<int>>& prerequisites) {
vector<int> in_degree(n, 0); // Array to store the in-degree of each task
(number of prerequisites)
vector<vector<int>> adj(n); // Adjacency list to store the graph (task
dependencies)

// Step 1: Build the graph and compute the in-degree for each task
for (auto& prereq : prerequisites) { // Iterate over each prerequisite pair
int task = prereq[0]; // Task that depends on another task
int dep = prereq[1]; // The task that must be completed first
(dependency)
adj[dep].push_back(task); // Add an edge from 'dep' to 'task'
in_degree[task]++; // Increment in-degree of 'task' since it depends on
'dep'
}

// Step 2: Initialize the queue with all tasks having in-degree 0 (no
prerequisites)
queue<int> q; // Queue to process tasks in topological order
for (int i = 0; i < n; i++) { // Loop over all tasks
if (in_degree[i] == 0) { // If the task has no prerequisites (in-degree is
0)
q.push(i); // Add this task to the queue
}
}
// Step 3: Process tasks in the queue and decrease in-degree of their
dependent tasks
int processed = 0; // Counter to keep track of how many tasks have been
processed
while (!q.empty()) { // While the queue is not empty
int task = q.front(); // Get the task at the front of the queue
q.pop(); // Remove the task from the queue
processed++; // Increment the number of processed tasks

// Decrease the in-degree of all tasks that depend on the current task
for (int next_task : adj[task]) { // For each dependent task
in_degree[next_task]--; // Reduce in-degree of the dependent task
if (in_degree[next_task] == 0) { // If the dependent task now has no
prerequisites
q.push(next_task); // Add it to the queue to be processed next
}
}
}

// Step 4: If the number of processed tasks is less than 'n', there's a cycle
return processed == n; // If all tasks are processed, return true; else, return
false
}

int main() {
int n = 2; // Number of tasks
// Prerequisites list where each pair (a, b) means task 'a' depends on task 'b'
vector<vector<int>> prerequisites = {{1, 0}};

// Call the function and print the result


if (canFinish(n, prerequisites)) { // Check if it's possible to finish all tasks
cout << "It is possible to finish all tasks." << endl; // Print if possible
} else {
cout << "It is not possible to finish all tasks." << endl; // Print if not
possible
}

return 0; // Return 0 to indicate successful execution of the program


}

You might also like