You are on page 1of 59

Experiment 4

Aim: Given a binary 2D matrix, find the number of islands.

Theory:A connected component of an undirected graph is a subgraph in which every two vertices are
connected to each other by a path(s), and which is connected to no other vertices outside the subgraph.

A graph where all vertices are connected with each other has exactly one connected component, consisting
of the whole graph. Such a graph with only one connected component is called a Strongly Connected Graph.
This problem can be easily solved by applying DFS() on each component. In each DFS() call, a component
or a sub-graph is visited. We will call DFS on the next un-visited component. The number of calls to DFS()
gives the number of connected components. BFS can also be used.
The idea is to keep an additional matrix to keep track of the visited nodes in the given matrix, and
perform DFS to find the total number of islands

Flow Chart:
Start
|
V
Initialize count = 0
|
V
Read input grid
|
V
For each cell in the grid:
|
| If cell is '1':
| |
| V
| Perform DFS or BFS to mark connected land cells as visited
| Increment count by 1
|
V
Display count
|
V
End

Code:-

#include <bits/stdc++.h>
using namespace std;

#define ROW 5
#define COL 5

// A function to check if a given


// cell (row, col) can be included in DFS
int isSafe(int M[][COL], int row, int col,
bool visited[][COL])
{
// row number is in range, column
// number is in range and value is 1
// and not yet visited
return (row >= 0) && (row < ROW) && (col >= 0)
&& (col < COL)
&& (M[row][col] && !visited[row][col]);
}

// A utility function to do DFS for a


// 2D boolean matrix. It only considers
// the 8 neighbours as adjacent vertices
void DFS(int M[][COL], int row, int col,
bool visited[][COL])
{
// These arrays are used to get
// row and column numbers of 8
// neighbours of a given cell
static int rowNbr[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
static int colNbr[] = { -1, 0, 1, -1, 1, -1, 0, 1 };

// Mark this cell as visited


visited[row][col] = true;

// Recur for all connected neighbours


for (int k = 0; k < 8; ++k)
if (isSafe(M, row + rowNbr[k], col + colNbr[k],
visited))
DFS(M, row + rowNbr[k], col + colNbr[k],
visited);
}

// The main function that returns


// count of islands in a given boolean
// 2D matrix
int countIslands(int M[][COL])
{
// Make a bool array to mark visited cells.
// Initially all cells are unvisited
bool visited[ROW][COL];
memset(visited, 0, sizeof(visited));

// Initialize count as 0 and


// traverse through the all cells of
// given matrix
int count = 0;
for (int i = 0; i < ROW; ++i)
for (int j = 0; j < COL; ++j)

// If a cell with value 1 is not


if (M[i][j] && !visited[i][j]) {
// visited yet, then new island found
// Visit all cells in this island.
DFS(M, i, j, visited);

// and increment island count


++count;
}
return count;
}

// Driver code
int main()
{
int M[][COL] = { { 1, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 1 },
{ 0, 0, 0, 0, 0 },
{ 1, 0, 1, 0, 1 } };

cout << "Number of islands is: " << countIslands(M);

return 0;
}
Output:

Conclusion:In conclusion, the flowchart for finding the number of islands in a grid provides a structured
visual representation of the algorithmic steps involved in solving this problem. By systematically looping
through each cell in the grid and employing a search algorithm like Depth-First Search (DFS) or Breadth-
First Search (BFS), we can efficiently identify and mark connected land cells, ultimately counting the
number of islands in the given grid.
Aim:Number of Provinces

Theory:
To find the number of provinces in a graph, we use depth-first search (DFS) or breadth-first search (BFS) to
traverse and mark connected components. The idea is to start from a city, explore all its connections, mark
them as visited, and repeat the process for unvisited cities until all cities have been explored. Each time we
initiate a new search, we increment the province count.

Flowchart:
Start
|
V
Initialize variables: n (number of cities), isConnected (adjacency matrix), visited, and provinces
|
V
Read n
|
V
Initialize isConnected as an n x n matrix
|
V
Read isConnected matrix
|
V
Define a function: DFS
|----|
| V
| Mark city as visited
| |
| V
| For each unvisited connected city
| |----|
| | V
| | Recursively call DFS
| |
| V
|
V
Initialize provinces as 0
|
V
For each unvisited city
|----|
| V
| Call DFS
| |
| V
| Increment provinces
| |
| V
|
V
Display the number of provinces
|
V
End

Code:
#include <iostream>
#include <vector>

using namespace std;

void dfs(vector<vector<int>>& isConnected, vector<bool>& visited, int currentCity) {


visited[currentCity] = true;
for (int i = 0; i < isConnected.size(); i++) {
if (isConnected[currentCity][i] == 1 && !visited[i]) {
dfs(isConnected, visited, i);
}
}
}

int findProvinces(vector<vector<int>>& isConnected) {


int n = isConnected.size();
vector<bool> visited(n, false);
int provinces = 0;

for (int i = 0; i < n; i++) {


if (!visited[i]) {
dfs(isConnected, visited, i);
provinces++;
}
}

return provinces;
}

int main() {
int n;
cout << "Enter the number of cities: ";
cin >> n;

vector<vector<int>> isConnected(n, vector<int>(n));

cout << "Enter the adjacency matrix representing the provinces (1 for connected, 0 for not connected):\n";
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> isConnected[i][j];
}
}

int provinces = findProvinces(isConnected);

cout << "The number of provinces is: " << provinces << endl;

return 0;
}
Output:
Enter the number of cities: 4
Enter the adjacency matrix representing the provinces (1 for connected, 0 for not connected):
1100
1101
0010
0101
The number of provinces is: 2

Conclusion:The provided flowchart illustrates the process of finding the number of provinces in a graph. It
uses depth-first search to explore connected cities, incrementing the province count with each new search.
The flowchart guides the step-by-step implementation of the algorithm.
In the example input and output I provided earlier, the program successfully identified the number of
provinces in the given adjacency matrix, which demonstrated the functionality of the algorithm. You can
apply this approach to various graph problems where you need to determine connected components.
Aim:Detect cycle in a undirected graph

Theory:In an undirected graph, a cycle is detected when you encounter an already visited vertex (excluding
the parent vertex of the current node) while performing a DFS. If a previously visited vertex (other than the
parent) is reachable from the current node, then a cycle exists.

Flowchart:
Start
|
V
Initialize variables: n (number of vertices), adj (adjacency list), visited, and parent
|
V
Read n
|
V
Initialize adj as an array of lists of integers
|
V
Read the graph edges and populate the adjacency list (adj)
|
V
Define a function: DFS
|----|
| V
| Mark the current node as visited
| |
| V
| For each neighbor of the current node
| |----|
| | V
| | If the neighbor is not visited and is not the parent
| | |----|
| | | V
| | | Call DFS recursively with the neighbor as the current node
| | | |
| | | V
| | | If the DFS returns true
| | | |----|
| | | | V
| | | | Return true (cycle detected)
| | | | |
| | | | V
| | | | End
| | | |----|
| | | |
| | | V
| | |----|
| | V
| V
|
V
Initialize visited and parent arrays
|
V
For each unvisited vertex
|----|
| V
| If DFS returns true
| |----|
| | V
| | Display "Cycle Detected"
| | |
| | V
| | End
| |----|
|
V
Display "No Cycle Detected"
|
V
End

Code:
#include <iostream>
#include <list>
#include <vector>

using namespace std;

class Graph {
public:
int vertices;
list<int> *adj;

Graph(int vertices);
void addEdge(int v, int w);
bool isCyclic();
bool isCyclicUtil(int v, bool visited[], int parent);
};

Graph::Graph(int vertices) {
this->vertices = vertices;
adj = new list<int>[vertices];
}

void Graph::addEdge(int v, int w) {


adj[v].push_back(w);
adj[w].push_back(v);
}

bool Graph::isCyclicUtil(int v, bool visited[], int parent) {


visited[v] = true;
list<int>::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i) {
if (!visited[*i]) {
if (isCyclicUtil(*i, visited, v)) {
return true;
} else if (*i != parent) {
return true;
}
}
}
return false;
}

bool Graph::isCyclic() {
bool *visited = new bool[vertices];
for (int i = 0; i < vertices; i++) {
visited[i] = false;
}

for (int i = 0; i < vertices; i++) {


if (!visited[i]) {
if (isCyclicUtil(i, visited, -1)) {
return true;
}
}
}
return false;
}

int main() {
int n, m;
cout << "Enter the number of vertices: ";
cin >> n;
cout << "Enter the number of edges: ";
cin >> m;

Graph g(n);
cout << "Enter the edges (vertices connected by an edge):" << endl;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
g.addEdge(u, v);
}

if (g.isCyclic()) {
cout << "Cycle Detected" << endl;
} else {
cout << "No Cycle Detected" << endl;
}

return 0;
}
Output:-
Enter the number of vertices: 4
Enter the number of edges: 4
Enter the edges (vertices connected by an edge):
01
12
23
30
Cycle Detected
Conclusion:
The provided flowchart and C++ code demonstrate how to detect cycles in an undirected graph using Depth-
First Search (DFS). When a cycle is detected, the program outputs "Cycle Detected," otherwise, it outputs
"No Cycle Detected." This algorithm is a fundamental tool for identifying cycles within graphs, which is
essential in various applications, including network analysis, route planning, and more.
Aim: Hamiltonian Cycle

Theory:

To find a Hamiltonian path in a graph, you typically use backtracking. Starting from a vertex, you explore
all possible paths while ensuring that each vertex is visited exactly once, and the path ends at a vertex
without revisiting any vertex. If you reach a point where no further extension is possible, you backtrack and
explore other options.

Flowchart:

The backtracking algorithm for Hamiltonian path is complex and may not fit into a simplified flowchart
format. However, here's a high-level representation:
Start
|
V
Initialize variables: n (number of vertices), adj (adjacency list), and path
|
V
Read n
|
V
Initialize adj as an array of lists of integers
|
V
Read the graph edges and populate the adjacency list (adj)
|
V
Define a function: HamiltonianPathUtil
|----|
| V
| Add the current vertex to the path
| |
| V
| If the path contains all vertices
| |----|
| | V
| | Display the Hamiltonian path
| | |
| | V
| | End
| |----|
| |
| V
| For each adjacent unvisited vertex
| |----|
| | V
| | Recursively call HamiltonianPathUtil with the adjacent vertex
| | |
| | V
| | End
| |----|
| |
| V
| Remove the current vertex from the path
| |
| V
|
V
Initialize path
|
V
For each vertex as a starting point
|----|
| V
| Call HamiltonianPathUtil with the vertex as the starting point
| |
| V
| If a Hamiltonian path is found, display it
| |
| V
| End
| |
| V
|
V
Display "No Hamiltonian Path found"
|
V
End

C++ Code:
#include <iostream>
#include <list>
#include <vector>
using namespace std;

class Graph {
public:
int vertices;
list<int> *adj;
vector<int> path;

Graph(int vertices);
void addEdge(int v, int w);
bool isHamiltonianPath();
bool isHamiltonianPathUtil(int v);
void displayHamiltonianPath();
};

Graph::Graph(int vertices) {
this->vertices = vertices;
adj = new list<int>[vertices];
}

void Graph::addEdge(int v, int w) {


adj[v].push_back(w);
adj[w].push_back(v);
}
bool Graph::isHamiltonianPathUtil(int v) {
if (path.size() == vertices) {
return true;
}

for (int u : adj[v]) {


if (find(path.begin(), path.end(), u) == path.end()) {
path.push_back(u);
if (isHamiltonianPathUtil(u)) {
return true;
}
path.pop_back();
}
}
return false;
}

bool Graph::isHamiltonianPath() {
path.clear();
for (int i = 0; i < vertices; i++) {
path.push_back(i);
if (isHamiltonianPathUtil(i)) {
return true;
}
path.pop_back();
}
return false;
}

void Graph::displayHamiltonianPath() {
if (path.size() == vertices) {
for (int vertex : path) {
cout << vertex << " ";
}
cout << endl;
} else {
cout << "No Hamiltonian Path found" << endl;
}
}

int main() {
int n, m;
cout << "Enter the number of vertices: ";
cin >> n;
cout << "Enter the number of edges: ";
cin >> m;

Graph g(n);
cout << "Enter the edges (vertices connected by an edge):" << endl;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
g.addEdge(u, v);
}
if (g.isHamiltonianPath()) {
cout << "Hamiltonian Path found: ";
g.displayHamiltonianPath();
} else {
cout << "No Hamiltonian Path found" << endl;
}

return 0;
}

Output:
Sample Input (with Hamiltonian Path):
Enter the number of vertices: 5
Enter the number of edges: 7
Enter the edges (vertices connected by an edge):
01
12
23
34
40
02
24

Sample Output (Hamiltonian Path Found):


Hamiltonian Path found: 0 1 2 3 4

Sample Input (No Hamiltonian Path):

Enter the number of vertices: 4


Enter the number of edges: 4
Enter the edges (vertices connected by an edge):
01
12
23
03
Sample Output (No Hamiltonian Path Found):

No Hamiltonian Path found

Conclusion:

The provided flowchart and C++ code demonstrate how to find a Hamiltonian path in a graph using a
backtracking approach. When a Hamiltonian path is found, the program outputs the path, and if no
Hamiltonian path exists, it outputs "No Hamiltonian Path found." This algorithm is an important tool in
solving problems related to path optimization and traversal in various applications, including transportation
and network analysis.
Aim: The aim of this algorithm is to determine whether a set of tasks with prerequisites can be completed in
a specific order while satisfying the prerequisites. The algorithm should detect if there is a valid order to
complete all tasks based on their dependencies.

Theory:

Given a set of tasks with prerequisites, this problem can be represented as a directed graph, where each task
is a node, and directed edges represent prerequisites. To solve this problem, we can use topological sorting,
which is a linear ordering of nodes in a directed acyclic graph (DAG) such that, for each directed edge (u,
v), node u comes before node v in the ordering.

The algorithm involves finding a topological ordering of the tasks while ensuring that the graph is a DAG. If
a topological ordering exists, it means the tasks can be completed in the specified order. If no topological
ordering is possible, it indicates that there is a cycle in the graph, and the tasks cannot be completed in a
valid order due to circular dependencies.

Simplified Flowchart:

Here's a simplified flowchart representing the algorithm:

Start
|
V
Initialize variables: n (number of tasks), prerequisites (list of prerequisites)
|
V
Create an adjacency list representation of the graph
|
V
Define a function: TopologicalSort
|----|
| V
| Initialize in-degrees and a queue
| |
| V
| Calculate in-degrees for all nodes
| |
| V
| Add nodes with in-degree 0 to the queue
| |
| V
| Initialize a result list
| |
| V
| While the queue is not empty
| |----|
| | V
| | Dequeue a node and add it to the result
| | |
| | V
| | Decrease in-degrees of adjacent nodes
| | |
| | V
| | If any node has an in-degree of 0, enqueue it
| | |
| | V
| |----|
| |
| V
| If the result list size is equal to n
| |----|
| | V
| | Display "Valid Order Exists"
| | |
| | V
| | End
| |----|
| |
| V
| Display "No Valid Order Exists (Cycle Detected)"
| |
| V
|----|
|
V
Call TopologicalSort function
|
V
End
C++ Code:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {


vector<vector<int>> graph(numCourses);
vector<int> inDegree(numCourses, 0);

for (auto& pre : prerequisites) {


graph[pre[1]].push_back(pre[0]);
inDegree[pre[0]]++;
}

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

int count = 0;
while (!q.empty()) {
int course = q.front();
q.pop();
count++;

for (int neighbor : graph[course]) {


if (--inDegree[neighbor] == 0) {
q.push(neighbor);
}
}
}

return count == numCourses;


}

int main() {
int n, m;
cout << "Enter the number of tasks: ";
cin >> n;
cout << "Enter the number of prerequisites: ";
cin >> m;

vector<vector<int>> prerequisites(m, vector<int>(2));


cout << "Enter the prerequisites (task, prerequisite):" << endl;
for (int i = 0; i < m; i++) {
cin >> prerequisites[i][0] >> prerequisites[i][1];
}

if (canFinish(n, prerequisites)) {
cout << "Valid Order Exists" << endl;
} else {
cout << "No Valid Order Exists (Cycle Detected)" << endl;
}

return 0;
}

Output:

Sample Input (Valid Order Exists):

mathematica
Copy code
Enter the number of tasks: 4
Enter the number of prerequisites: 2
Enter the prerequisites (task, prerequisite):
10
21
Sample Output (Valid Order Exists):

Valid Order Exists


Sample Input (No Valid Order Exists - Cycle Detected):

Enter the number of tasks: 4


Enter the number of prerequisites: 3
Enter the prerequisites (task, prerequisite):
10
21
02
Sample Output (No Valid Order Exists - Cycle Detected):
No Valid Order Exists (Cycle Detected)

Conclusion:

The aim of this algorithm is to determine whether a set of tasks with prerequisites can be completed in a
specific order while satisfying the prerequisites. The algorithm uses topological sorting to check if a valid
order exists. If a valid order is found, it indicates that the tasks can be completed in the specified order. If no
valid order is found, it means that there is a cycle in the graph, and the tasks cannot be completed due to
circular dependencies. This algorithm is valuable for project scheduling, course prerequisites, and task
management.
Aim:
The aim of this algorithm is to determine whether a set of courses with prerequisites can be scheduled in a
specific order while satisfying the prerequisites.

Theory:
The course scheduling problem can be approached in the same way as the prerequisite task problem, where
you check if there is a valid topological ordering of courses. If a valid ordering exists, it means that the
courses can be scheduled in the specified order. If no valid ordering exists, it indicates that there is a cycle in
the graph of courses, and the courses cannot be scheduled due to circular dependencies.

Simplified Flowchart:

1. Start
2. Initialize variables: n (number of courses), prerequisites (list of prerequisites)
3. Create an adjacency list representation of the graph
4. Define a function: TopologicalSort
a. Initialize in-degrees and a queue
b. Calculate in-degrees for all courses
c. Add courses with in-degree 0 to the queue
d. Initialize a result list
e. While the queue is not empty
i. Dequeue a course and add it to the result
ii. Decrease in-degrees of adjacent courses
iii. If any course has an in-degree of 0, enqueue it
f. If the result list size is equal to n
i. Display "Valid Course Schedule Exists"
g. Else
i. Display "No Valid Course Schedule Exists (Cycle Detected)"
5. Call TopologicalSort function
6. End

C++ Code:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {


vector<vector<int>> graph(numCourses);
vector<int> inDegree(numCourses, 0);

for (auto& pre : prerequisites) {


graph[pre[1]].push_back(pre[0]);
inDegree[pre[0]]++;
}

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

int count = 0;
while (!q.empty()) {
int course = q.front();
q.pop();
count++;

for (int neighbor : graph[course]) {


if (--inDegree[neighbor] == 0) {
q.push(neighbor);
}
}
}

return count == numCourses;


}

int main() {
int n, m;
cout << "Enter the number of courses: ";
cin >> n;
cout << "Enter the number of prerequisites: ";
cin >> m;

vector<vector<int>> prerequisites(m, vector<int>(2));


cout << "Enter the prerequisites (course, prerequisite):" << endl;
for (int i = 0; i < m; i++) {
cin >> prerequisites[i][0] >> prerequisites[i][1];
}

if (canFinish(n, prerequisites)) {
cout << "Valid Course Schedule Exists" << endl;
} else {
cout << "No Valid Course Schedule Exists (Cycle Detected)" << endl;
}

return 0;
}

Output:
Sample Input (Valid Course Schedule Exists):

Enter the number of courses: 4


Enter the number of prerequisites: 2
Enter the prerequisites (course, prerequisite):
10
21

Sample Output (Valid Course Schedule Exists):

Valid Course Schedule Exists

Sample Input (No Valid Course Schedule Exists - Cycle Detected):

Enter the number of courses: 4


Enter the number of prerequisites: 3
Enter the prerequisites (course, prerequisite):
10
21
02

Sample Output (No Valid Course Schedule Exists - Cycle Detected):

No Valid Course Schedule Exists (Cycle Detected)

Conclusion:
The course scheduling algorithm aims to determine whether a set of courses with prerequisites can be
scheduled in a specific order while satisfying the prerequisites. It utilizes topological sorting to check if a
valid schedule exists. If a valid schedule is found, it means the courses can be scheduled in the specified
order. If no valid schedule is found, it indicates that there is a cycle in the course graph, and the courses
cannot be scheduled due to circular dependencies. This algorithm is crucial for academic institutions and
online learning platforms to ensure that students can enroll in courses without conflicting prerequisites.
Aim:
The aim of this algorithm is to determine whether a given set of strings can form a circle, where the last
character of one string matches the first character of the next string.

Theory:
This problem can be viewed as finding an Eulerian path or circuit in a directed graph. Each string represents
a node in the graph, and an edge connects two nodes if the last character of the first string matches the first
character of the second string. If the graph has an Eulerian circuit (a circuit that visits every edge exactly
once and ends at the starting node), it means the strings can form a circle.

Simplified Flowchart:

Start
|
V
Initialize variables: strings (list of strings), graph (adjacency list), inDegree (in-degrees), visited (visited
nodes), hasEulerianCircuit
|
V
Create adjacency list and in-degrees based on the first and last characters of the strings
|
V
Define a function: DFS
|----|
| V
| Mark the current node as visited
| |
| V
| For each unvisited neighbor
| |----|
| | V
| | Recursively call DFS on the neighbor
| | |
| | V
| | End
| |----|
| |
| V
| If hasEulerianCircuit is true
| |----|
| | V
| Return
| | |
| | V
| End
| |----|
| |
| V
| For each neighbor
| |----|
| | V
| Decrease in-degree of neighbor
| | |
| | V
| If in-degree is 0, call DFS recursively on the neighbor
| | |
| | V
| End
| |----|
| |
| V
| End
|----|
|
V
Initialize visited nodes
|
V
For each unvisited node
|----|
| V
| Call DFS on the node
| | |
| | V
| If visited nodes are less than the total nodes, set hasEulerianCircuit to false
| | |
| | V
| End
|----|
|
V
If hasEulerianCircuit is true
|----|
| V
| Display "Circle of Strings Exists"
| |
| V
| End
|----|
|
V
Display "No Circle of Strings Exists"
|
V
End
```

C++ Code:
Here's a C++ program to check if a circle of strings can be formed:
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

bool canFormCircleOfStrings(vector<string>& strings) {


unordered_map<char, vector<string>> firstCharToNodes, lastCharToNodes;
unordered_map<string, vector<string>> graph;
unordered_map<string, int> inDegree;
bool hasEulerianCircuit = true;
for (const string& s : strings) {
char firstChar = s[0];
char lastChar = s[s.length() - 1];
firstCharToNodes[firstChar].push_back(s);
lastCharToNodes[lastChar].push_back(s);
graph[s].insert(graph[s].end(), lastCharToNodes[firstChar].begin(), lastCharToNodes[firstChar].end());
inDegree[s] = lastCharToNodes[firstChar].size();
}

unordered_map<string, bool> visited;


for (const string& s : strings) {
visited[s] = false;
}

for (const string& s : strings) {


if (!visited[s]) {
function<void(string)> dfs = [&](string node) {
visited[node] = true;
if (hasEulerianCircuit) {
return;
}
for (const string& neighbor : graph[node]) {
if (!visited[neighbor]) {
dfs(neighbor);
}
}
if (hasEulerianCircuit) {
return;
}
for (const string& neighbor : graph[node]) {
inDegree[neighbor]--;
if (inDegree[neighbor] == 0) {
dfs(neighbor);
}
}
};

dfs(s);
if (visited.size() < strings.size()) {
hasEulerianCircuit = false;
break;
}
}
}

return hasEulerianCircuit;
}

int main() {
vector<string> strings = {"abc", "cd", "cda", "daa"};
bool result = canFormCircleOfStrings(strings);

if (result) {
cout << "Circle of Strings Exists"
```cpp
<< endl;
} else {
cout << "No Circle of Strings Exists" << endl;
}

return 0;
}

Output:

Sample Input (Circle of Strings Exists):

vector<string> strings = {"abc", "cd", "cda", "daa"};

Sample Output (Circle of Strings Exists):


Circle of Strings Exists

Conclusion:
The "Circle of Strings" algorithm aims to determine whether a given set of strings can form a circle, where
the last character of one string matches the first character of the next string. It leverages the concept of
finding an Eulerian circuit in a directed graph, and if such a circuit exists, it indicates that the strings can
form a circle. This problem is relevant in various scenarios, such as word games and string manipulations.
Aim:
The aim of this algorithm is to determine the minimum number of moves required to reach the final position
on a Snake and Ladder board starting from position 1.

Theory:
The Snake and Ladder game is represented as a directed graph where each cell is a node, and the edges
represent possible moves. Snakes and Ladders are equivalent to additional edges that connect the nodes. The
problem can be solved using graph traversal algorithms, such as Breadth-First Search (BFS), to find the
shortest path to the final position.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm:

Start
|
V
Initialize variables: board (list of moves), queue (BFS queue), visited (set of visited nodes), and moves
|
V
Enqueue the starting position (1) to the queue with moves = 0
|
V
While the queue is not empty
|----|
| V
| Dequeue a position and moves
| |
| V
| If the final position is reached
| |----|
| | V
| Display the minimum moves
| | |
| | V
| End
| |----|
| |
| V
| For each possible move from the current position
| |----|
| | V
| Calculate the new position after the move
| | |
| | V
| If the new position is not visited
| | |----|
| | | V
| | | Enqueue the new position with moves + 1
| | | |
| | | V
| | | Mark the new position as visited
| | |----|
| | |
| | V
| |----|
| |
| V
|----|
|
V
Display "No path to the final position"
|
V
End
```

Code:

from collections import deque

def minMovesToFinish(board):
n = len(board)
visited = set()
queue = deque([(1, 0)])

while queue:
position, moves = queue.popleft()

if position == n:
return moves

for i in range(1, 7):


new_position = position + i
if new_position <= n and new_position not in visited:
visited.add(new_position)
if board[new_position] != -1:
new_position = board[new_position]
queue.append((new_position, moves + 1))

return -1

# Example usage
board = [-1, 3, -1, -1, -1, -1, -1, 6, -1, -1, -1]
result = minMovesToFinish(board)
if result != -1:
print(f"Minimum moves to finish: {result}")
else:
print("No path to the final position")

Output:

Sample Input:
board = [-1, 3, -1, -1, -1, -1, -1, 6, -1, -1, -1]

Sample Output:
Minimum moves to finish: 3
Conclusion:
The Snake and Ladder problem involves finding the minimum number of moves to reach the final position
on a game board. The algorithm utilizes Breadth-First Search (BFS) to explore possible moves efficiently
and determine the shortest path. The concept can be extended to solve variations of the game and is
applicable in various gaming scenarios and simulations.
Aim:
The aim of this algorithm is to determine whether a given graph is a Bipartite Graph or not.

Theory:
A graph is bipartite if it can be divided into two disjoint sets such that no two vertices within the same set are
connected by an edge. This problem can be solved using graph traversal, specifically by using Depth-First
Search (DFS) or Breadth-First Search (BFS). We assign colors to the vertices of the graph in an alternating
manner to check if adjacent vertices have different colors. If at any point two adjacent vertices have the
same color, the graph is not bipartite.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm:

Start
|
V
Initialize variables: graph (adjacency list), visited (set of visited nodes), colors (array of vertex colors)
|
V
For each unvisited vertex in the graph
|----|
| V
| Initialize a BFS/DFS queue or stack
| |
| V
| Enqueue the current vertex to the queue or push it to the stack with color 0
| |
| V
| While the queue or stack is not empty
| |----|
| | V
| Dequeue or pop a vertex and its color
| | |
| | V
| Mark the vertex as visited and record its color
| | |
| | V
| For each adjacent vertex
| | |----|
| | | V
| | | If the adjacent vertex is not visited
| | | | |----|
| | | | | V
| | | | | Assign a different color to the adjacent vertex
| | | | | |
| | | | | V
| | | | | Enqueue or push the adjacent vertex with its color
| | | | | |----|
| | | | |----|
| | | |
| | |----|
| | |
| | V
| |----|
| |
| V
|----|
|
V
If all vertices have been visited without conflict, display "Graph is Bipartite"
|
V
If a conflict is encountered, display "Graph is not Bipartite"
|
V
End

C++ Code
Here's a C++ program to check if a graph is bipartite:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

bool isBipartite(vector<vector<int>>& graph) {


int n = graph.size();
vector<int> colors(n, -1); // -1 for uncolored, 0 and 1 for two different colors
vector<bool> visited(n, false);

for (int i = 0; i < n; i++) {


if (!visited[i]) {
queue<int> q;
q.push(i);
colors[i] = 0;
visited[i] = true;

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

for (int neighbor : graph[current]) {


if (!visited[neighbor]) {
colors[neighbor] = 1 - colors[current]; // Assign a different color
visited[neighbor] = true;
q.push(neighbor);
} else if (colors[neighbor] == colors[current]) {
return false; // Conflict: The graph is not bipartite
}
}
}
}
}

return true;
}

int main() {
vector<vector<int>> graph = {{1, 3}, {0, 2}, {1, 3}, {0, 2}};
if (isBipartite(graph)) {
cout << "Graph is Bipartite" << endl;
} else {
cout << "Graph is not Bipartite" << endl;
}

return 0;
}

Output:
Sample Input (Bipartite Graph):
vector<vector<int>> graph = {{1, 3}, {0, 2}, {1, 3}, {0, 2}};

Sample Output (Bipartite Graph):


Graph is Bipartite

Conclusion:
The Bipartite Graph algorithm aims to determine whether a given graph can be divided into two disjoint sets
such that no two vertices within the same set are connected by an edge. It utilizes graph traversal (BFS or
DFS) to assign colors to vertices in an alternating manner, checking for conflicts during the traversal. If no
conflicts are encountered, the graph is considered bipartite. This problem has applications in various areas,
including network design and modeling.
Aim:
The aim of this algorithm is to find the largest possible matching in a given bipartite graph.

Theory:
In a bipartite graph, a matching is a set of edges that do not share vertices. The maximum bipartite matching
problem can be solved using various algorithms, such as the Hopcroft-Karp algorithm, Ford-Fulkerson
algorithm, or the Hungarian algorithm. These algorithms iteratively augment the matching by finding
augmenting paths in the graph until no more augmenting paths can be found.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm:

Start
|
V
Initialize variables: graph (bipartite graph), match (array to store matching), visited (array to track visited
nodes), augmenting paths
|
V
While there is an augmenting path in the graph
|----|
| V
| Initialize visited array
| |
| V
| For each vertex in the first set of the bipartite graph
| |----|
| | V
| | If the vertex is unmatched
| | | |----|
| | | | V
| | | | Find an augmenting path starting from this vertex
| | | | |
| | | | V
| | | | If an augmenting path is found
| | | | | |----|
| | | | | V
| | | | | Increment the matching size
| | | | | |
| | | | | V
| | | | | Mark all vertices in the augmenting path as visited
| | | | |----|
| | | |
| | |----|
| |
| V
|----|
|
V
Display the maximum bipartite matching
|
V
End
C++ Code:
Here's a C++ program to find the maximum bipartite matching using the Hopcroft-Karp algorithm:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

const int NIL = 0;


const int INF = INT_MAX;

bool bfs(vector<vector<int>>& graph, vector<int>& match, vector<int>& dist) {


int n = graph.size();
queue<int> q;

for (int u = 1; u < n / 2; u++) {


if (match[u] == NIL) {
dist[u] = 0;
q.push(u);
} else {
dist[u] = INF;
}
}

dist[NIL] = INF;

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

if (u != NIL) {
for (int v : graph[u]) {
if (dist[match[v]] == INF) {
dist[match[v]] = dist[u] + 1;
q.push(match[v]);
}
}
}
}

return (dist[NIL] != INF);


}

bool dfs(vector<vector<int>>& graph, vector<int>& match, vector<int>& dist, int u) {


if (u != NIL) {
for (int v : graph[u]) {
if (dist[match[v]] == dist[u] + 1 && dfs(graph, match, dist, match[v])) {
match[u] = v;
match[v] = u;
return true;
}
}
dist[u] = INF;
return false;
}
return true;
}

int hopcroftKarp(vector<vector<int>>& graph) {


int n = graph.size();
vector<int> match(n, NIL);
vector<int> dist(n);

int matchingSize = 0;
while (bfs(graph, match, dist)) {
for (int u = 1; u < n / 2; u++) {
if (match[u] == NIL && dfs(graph, match, dist, u)) {
matchingSize++;
}
}
}

return matchingSize;
}

int main() {
vector<vector<int>> graph = {
{}, {3, 4}, {3}, {4}
};

int maxMatching = hopcroftKarp(graph);

cout << "Maximum Bipartite Matching Size: " << maxMatching << endl;

return 0;
}

Output:
Sample Input:

vector<vector<int>> graph = {
{}, {3, 4}, {3}, {4}
};

Sample Output:
Maximum Bipartite Matching Size: 2

Conclusion:
The Maximum Bipartite Matching algorithm aims to find the largest possible matching in a bipartite graph.
It is a fundamental problem in graph theory with applications in various fields, including network flow,
assignment problems, and resource allocation. The Hopcroft-Karp algorithm is one of the efficient methods
to solve this problem, and it iteratively finds augmenting paths to maximize the matching size.
Aim:
The aim of this algorithm is to determine whether a given graph contains a cycle.

Theory
A cycle in a graph is a path that starts and ends at the same vertex and consists of at least one edge. To detect
cycles in a graph, we can use depth-first search (DFS) or breadth-first search (BFS). During the traversal, if
we encounter a previously visited node that is not the parent of the current node (in the case of DFS), it
indicates the presence of a cycle.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm:

Start
|
V
Initialize variables: graph (adjacency list), visited (array to track visited nodes), parent (array to track parent
of nodes)
|
V
For each unvisited vertex in the graph
|----|
| V
| Initialize DFS stack or BFS queue
| |
| V
| Push or enqueue the current vertex to the stack or queue
| |
| V
| Mark the current vertex as visited and set its parent
| |
| V
| While the stack or queue is not empty
| |----|
| | V
| Pop or dequeue a vertex
| | |
| | V
| For each adjacent vertex
| | |----|
| | | V
| | | If the adjacent vertex is not visited
| | | | |----|
| | | | | V
| | | | | Mark the adjacent vertex as visited and set its parent
| | | | | |
| | | | | V
| | | | | Push or enqueue the adjacent vertex
| | | | | |----|
| | | | |----|
| | |
| |----|
| |
| V
|----|
|
V
If a previously visited vertex is encountered and not the parent, display "Cycle exists"
|
V
If all vertices have been visited without encountering a cycle, display "No cycle exists"
|
V
End

C++ Code:
Here's a C++ program to detect cycles in a graph using Depth-First Search (DFS):

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

bool hasCycleDFS(vector<vector<int>>& graph, int v, vector<bool>& visited, vector<int>& parent) {


stack<pair<int, int>> s;
s.push({v, -1}); // Pair: {vertex, parent}

while (!s.empty()) {
int current = s.top().first;
int p = s.top().second;
s.pop();

visited[current] = true;
parent[current] = p;

for (int neighbor : graph[current]) {


if (!visited[neighbor]) {
s.push({neighbor, current});
} else if (neighbor != p) {
return true; // Cycle found
}
}
}

return false;
}

bool hasCycle(vector<vector<int>>& graph) {


int n = graph.size();
vector<bool> visited(n, false);
vector<int> parent(n, -1);

for (int i = 0; i < n; i++) {


if (!visited[i]) {
if (hasCycleDFS(graph, i, visited, parent)) {
return true;
}
}
}
return false;
}

int main() {
vector<vector<int>> graph = {{1, 2}, {0, 3, 4}, {0, 4}, {1}, {1, 2}};

if (hasCycle(graph)) {
cout << "Cycle exists in the graph." << endl;
} else {
cout << "No cycle exists in the graph." << endl;
}

return 0;
}

Output:
Sample Input (Cycle Exists):

vector<vector<int>> graph = {{1, 2}, {0, 3, 4}, {0, 4}, {1}, {1, 2}};

Sample Output (Cycle Exists):

Cycle exists in the graph.

Conclusion:
The algorithm for detecting cycles in a graph, based on DFS or BFS traversal, is a fundamental problem in
graph theory. It is used in various applications, such as finding cycles in computer networks, verifying the
consistency of database schemas, and many more. The presence of a cycle can have different implications in
different contexts.
Aim:
The aim of this algorithm is to find the shortest path between two specified vertices in a graph.

Theory:
The problem of finding the shortest path in a graph can be solved using various algorithms, depending on
the characteristics of the graph and edge weights. Dijkstra's algorithm is suitable for graphs with non-
negative edge weights, while the Bellman-Ford algorithm can handle graphs with both positive and negative
weights. Both algorithms utilize graph traversal to find the shortest path.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for finding the shortest path using Dijkstra's
algorithm (for non-negative edge weights):

Start
|
V
Initialize variables: graph (adjacency list), source vertex, destination vertex, distances (array of distances),
previous nodes
|
V
Set all distances to infinity and previous nodes to -1
|
V
Set the distance of the source vertex to 0
|
V
Create a priority queue and enqueue the source vertex with distance 0
|
V
While the priority queue is not empty
|----|
| V
| Dequeue a vertex with the shortest distance
| |
| V
| If the dequeued vertex is the destination vertex
| |----|
| | V
| Display the shortest distance from source to destination
| | |
| | V
| Reconstruct the shortest path from source to destination using previous nodes
| | |
| | V
| End
| |----|
| |
| V
| For each neighbor of the dequeued vertex
| |----|
| | V
| Calculate the distance to the neighbor through the dequeued vertex
| | |
| | V
| If the calculated distance is shorter than the recorded distance
| | |----|
| | | V
| Update the distance and previous node for the neighbor
| | | |
| | | V
| Enqueue the neighbor with the updated distance
| | |----|
| |----|
| |
|----|
|
V
If the destination vertex is not reached, display "No path exists"
|
V
End

C++ Code (Using Dijkstra's Algorithm):


Here's a C++ program to find the shortest path between two vertices in a graph using Dijkstra's algorithm:

#include <iostream>
#include <vector>
#include <queue>
#include <limits>
using namespace std;

const int INF = numeric_limits<int>::max();

struct Edge {
int to, weight;
};

vector<int> dijkstra(vector<vector<Edge>>& graph, int source, int destination) {


int n = graph.size();
vector<int> distances(n, INF);
vector<int> previous(n, -1);
distances[source] = 0;

priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;


pq.push({0, source});

while (!pq.empty()) {
int dist = pq.top().first;
int u = pq.top().second;
pq.pop();

if (u == destination) {
vector<int> path;
int node = destination;
while (node != -1) {
path.push_back(node);
node = previous[node];
}
reverse(path.begin(), path.end());
return path;
}

if (dist > distances[u]) {


continue;
}

for (const Edge& edge : graph[u]) {


int v = edge.to;
int weight = edge.weight;
if (dist + weight < distances[v]) {
distances[v] = dist + weight;
previous[v] = u;
pq.push({distances[v], v});
}
}
}

return vector<int>(); // No path exists


}

int main() {
int n = 5;
vector<vector<Edge>> graph(n);
graph[0].push_back({1, 2});
graph[0].push_back({2, 4});
graph[1].push_back({2, 1});
graph[1].push_back({3, 7});
graph[2].push_back({3, 3});
graph[2].push_back({4, 5});
graph[3].push_back({4, 2});

int source = 0;
int destination = 4;

vector<int> shortestPath = dijkstra(graph, source, destination);

if (shortestPath.empty()) {
cout << "No path exists." << endl;
} else {
cout << "Shortest path from " << source << " to " << destination << ": ";
for (int node : shortestPath) {
cout << node << " ";
}
cout << endl;
}

return 0;
}

Output:
Sample Output (Shortest Path):

Shortest path from 0 to 4: 0 2 3 4


Sample Output (No Path Exists):

No path exists.

Conclusion:
The algorithm for finding the shortest path in a graph, based on Dijkstra's algorithm, is a fundamental
problem in graph theory. It has numerous applications in network routing, GPS navigation, and optimization
problems. The algorithm efficiently computes the shortest path between two vertices, considering non-
negative edge weights.
Aim:
The aim of this algorithm is to find a topological ordering of the vertices in a directed acyclic graph (DAG).

Theory:
Topological sorting can be achieved using depth-first search (DFS) or Kahn's algorithm. In DFS-based
topological sorting, we start from an unvisited vertex, explore its neighbors, and backtrack when no
unvisited neighbors remain. In Kahn's algorithm, we iteratively remove vertices with no incoming edges
from the graph.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for topological sorting using Kahn's algorithm:

Start
|
V
Initialize variables: graph (adjacency list), inDegree (array of in-degrees), queue (for vertices with in-degree
0), and topologicalOrder (array)
|
V
Calculate in-degrees for all vertices
|
V
Enqueue vertices with in-degree 0 to the queue
|
V
While the queue is not empty
|----|
| V
| Dequeue a vertex and add it to the topological order
| |
| V
| For each neighbor of the dequeued vertex
| |----|
| | V
| | Decrement the in-degree of the neighbor
| | |
| | V
| | If the neighbor's in-degree becomes 0, enqueue it
| |----|
| |
|----|
|
V
If the topological order contains all vertices, display the topological order
|
V
If not all vertices are included, a cycle exists; display "No topological order (graph contains a cycle)"
|
V
End
```

C++ Code (Using Kahn's Algorithm):


Here's a C++ program to perform topological sorting using Kahn's algorithm:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

vector<int> topologicalSort(vector<vector<int>>& graph) {


int n = graph.size();
vector<int> inDegree(n, 0);

// Calculate in-degrees for all vertices


for (int u = 0; u < n; u++) {
for (int v : graph[u]) {
inDegree[v]++;
}
}

queue<int> q;
for (int u = 0; u < n; u++) {
if (inDegree[u] == 0) {
q.push(u);
}
}

vector<int> topologicalOrder;
while (!q.empty()) {
int u = q.front();
q.pop();
topologicalOrder.push_back(u);

for (int v : graph[u]) {


inDegree[v]--;
if (inDegree[v] == 0) {
q.push(v);
}
}
}

if (topologicalOrder.size() == n) {
return topologicalOrder;
} else {
return vector<int>(); // Graph contains a cycle
}
}

int main() {
int n = 6;
vector<vector<int>> graph(n);
graph[0].push_back(1);
graph[0].push_back(2);
graph[1].push_back(3);
graph[2].push_back(3);
graph[2].push_back(4);
graph[3].push_back(5);
graph[4].push_back(5);
vector<int> result = topologicalSort(graph);

if (result.empty()) {
cout << "No topological order (graph contains a cycle)." << endl;
} else {
cout << "Topological Order: ";
for (int vertex : result) {
cout << vertex << " ";
}
cout << endl;
}

return 0;
}
Output:

Sample Output (Topological Order):


Topological Order: 0 1 2 3 4 5

Sample Output (Graph Contains a Cycle):


No topological order (graph contains a cycle).

Conclusion:
Topological sorting is a fundamental algorithm for linearly ordering the vertices in a directed acyclic graph.
It is commonly used in scheduling tasks with dependencies and other applications where ordering is
essential. The algorithm, whether implemented using Kahn's algorithm or DFS, efficiently identifies a valid
topological order or reports the presence of a cycle in the graph.

\\
Aim
The aim of this algorithm is to determine the level (depth) of each node in a binary tree, indicating its
distance from the root node.

Theory:
To find the levels of nodes in a binary tree, we can perform a Breadth-First Search (BFS) traversal. Starting
from the root node, we enqueue it along with its level (which is initially 0) into a queue. We then dequeue
nodes from the queue one by one, incrementing the level for their children as we encounter them. By the end
of the traversal, we will have the level of each node.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for finding the levels of nodes in a binary tree:

Start
|
V
Initialize variables: root (the root node), queue (for BFS), and level (initialized to 0)
|
V
Enqueue the root node with level 0
|
V
While the queue is not empty
|----|
| V
| Dequeue a node and its level
| |
| V
| Process the node: Record its level
| |
| V
| Enqueue its children with level+1
| |----|
| | V
| | If there are left and right children
| | | |----|
| | | | V
| | | | Enqueue the left child with level+1
| | | | |
| | | | V
| | | | Enqueue the right child with level+1
| | | |----|
| |----|
|
|----|
|
V
End
```

C++ Code:
Here's a C++ program to find the levels of nodes in a binary tree using BFS:

#include <iostream>
#include <queue>
using namespace std;

struct TreeNode {
int value;
TreeNode* left;
TreeNode* right;
TreeNode(int val) : value(val), left(nullptr), right(nullptr) {}
};

void findNodeLevels(TreeNode* root) {


if (root == nullptr) {
return;
}

queue<pair<TreeNode*, int>> q; // Pair: {node, level}


q.push({root, 0});

while (!q.empty()) {
TreeNode* node = q.front().first;
int level = q.front().second;
q.pop();

cout << "Node " << node->value << " is at level " << level << endl;

if (node->left) {
q.push({node->left, level + 1});
}

if (node->right) {
q.push({node->right, level + 1});
}
}
}

int main() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);

findNodeLevels(root);

return 0;
}

Output

Sample Output (Node Levels):

Node 1 is at level 0
Node 2 is at level 1
Node 3 is at level 1
Node 4 is at level 2
Node 5 is at level 2
Node 6 is at level 2
Node 7 is at level 2
```

Conclusion:
The algorithm for finding the levels of nodes in a binary tree using BFS is a fundamental operation in tree
traversal. It provides valuable information about the depth or distance of each node from the root. This
information is crucial for various tree-related tasks, such as determining the height of the tree or finding
common ancestors. The BFS traversal ensures that nodes are processed in level order, making it suitable for
such tasks.
Aim:
The aim of this algorithm is to find a mother vertex in a directed graph if one exists.

Theory:
To find a mother vertex in a directed graph, we can perform a Depth-First Search (DFS) traversal and keep
track of the last visited vertex during the traversal. If all vertices are visited during the DFS and the last
visited vertex can reach all other vertices, it is a mother vertex.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for finding a mother vertex in a directed graph:

Start
|
V
Initialize variables: graph (adjacency list), visited (array to track visited vertices), lastVisited (last visited
vertex)
|
V
For each vertex in the graph
|----|
| V
| If the vertex is not visited
| |----|
| | V
| | Perform a DFS from the vertex
| | |
| | V
| If all vertices are visited during the DFS
| |----|
| V
| The last visited vertex is a mother vertex
| |
| V
| End
|----|
|
V
End

Code
#include <iostream>
#include <vector>
using namespace std;

class Graph {
private:
vector<vector<int>> adjacencyList;
int vertices;

void dfs(int vertex, vector<bool>& visited) {


visited[vertex] = true;
for (int neighbor : adjacencyList[vertex]) {
if (!visited[neighbor]) {
dfs(neighbor, visited);
}
}
}

public:
Graph(int v) : vertices(v) {
adjacencyList.resize(vertices);
}

void addEdge(int u, int v) {


adjacencyList[u].push_back(v);
}

int findMotherVertex() {
vector<bool> visited(vertices, false);
int lastVisitedVertex = 0;

for (int i = 0; i < vertices; i++) {


if (!visited[i]) {
dfs(i, visited);
lastVisitedVertex = i;
}
}

fill(visited.begin(), visited.end(), false);


dfs(lastVisitedVertex, visited);

for (int i = 0; i < vertices; i++) {


if (!visited[i]) {
return -1; // No mother vertex found
}
}

return lastVisitedVertex;
}
};

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

int motherVertex = graph.findMotherVertex();

if (motherVertex != -1) {
cout << "A mother vertex is: " << motherVertex << endl;
} else {
cout << "No mother vertex found in the graph." << endl;
}
return 0;
}

Output:

Sample Output (Mother Vertex Exists):


A mother vertex is: 5

Conclusion:
The algorithm for finding a mother vertex in a directed graph is useful for identifying a vertex from which
you can reach all other vertices in the graph. It can be applied in various contexts, such as identifying critical
points in network connectivity or dependency analysis. The algorithm efficiently performs a Depth-First
Search (DFS) traversal to determine the presence of a mother vertex.
Aim:
The aim of this algorithm is to find the largest area of a contiguous region of 1s (connected component) in a
binary matrix and calculate its unit area.

Theory:
To find the largest area of a contiguous region of 1s, we can perform a depth-first search (DFS) or breadth-
first search (BFS) traversal of the binary matrix. During the traversal, we track the size of each connected
component, and the largest size encountered is the largest region.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for finding the largest area of a contiguous region of
1s:

Start
|
V
Initialize variables: binaryMatrix, visitedMatrix, maxSize (initialized to 0)
|
V
For each cell in binaryMatrix
|----|
| V
| If the cell is 1 and not visited
| |----|
| | V
| | Perform a DFS or BFS to find the size of the connected component
| | |
| | V
| | Update maxSize if the size is larger
| |----|
|----|
|
V
The maxSize is the largest area of 1s
|
V
Display the largest area (maxSize)
|
V
End

C++ Code (Finding the Largest Area of 1s in a Binary Matrix):


Here's a C++ program to find and calculate the largest area of a contiguous region of 1s in a binary matrix
using depth-first search (DFS):

#include <iostream>
#include <vector>
using namespace std;

int dfs(vector<vector<int>>& matrix, vector<vector<bool>>& visited, int row, int col) {


if (row < 0 || row >= matrix.size() || col < 0 || col >= matrix[0].size() || visited[row][col] || matrix[row][col]
== 0) {
return 0;
}
visited[row][col] = true;
int size = 1;

// Explore neighbors
size += dfs(matrix, visited, row - 1, col); // Up
size += dfs(matrix, visited, row + 1, col); // Down
size += dfs(matrix, visited, row, col - 1); // Left
size += dfs(matrix, visited, row, col + 1); // Right

return size;
}

int findLargestAreaOfOnes(vector<vector<int>>& matrix) {


if (matrix.empty() || matrix[0].empty()) {
return 0;
}

int rows = matrix.size();


int cols = matrix[0].size();
vector<vector<bool>> visited(rows, vector<bool>(cols, false));
int maxSize = 0;

for (int row = 0; row < rows; row++) {


for (int col = 0; col < cols; col++) {
if (!visited[row][col] && matrix[row][col] == 1) {
int size = dfs(matrix, visited, row, col);
maxSize = max(maxSize, size);
}
}
}

return maxSize;
}

int main() {
vector<vector<int>> binaryMatrix = {
{1, 1, 0, 0, 0},
{1, 1, 0, 1, 1},
{0, 0, 1, 1, 1},
{1, 0, 0, 1, 1},
{1, 1, 1, 0, 0}
};

int largestArea = findLargestAreaOfOnes(binaryMatrix);

cout << "The largest area of 1s in the binary matrix is: " << largestArea << " unit squares." << endl;

return 0;
}
Output:

Sample Output:
The largest area of 1s in the binary matrix is: 7 unit squares.
Conclusion:
The algorithm for finding the largest area of a contiguous region of 1s in a binary matrix is essential for
identifying connected components in a binary image. In this case, we use depth-first search (DFS) to
efficiently calculate the size of each connected component and find the largest one. This program can be
applied in various contexts, such as image processing, computer vision, and pattern recognition.
Aim:
The aim of this algorithm is to find the minimum time required for all oranges in a grid to become rotten. If
it's not possible for all oranges to become rotten, return -1.

Theory:
To solve the Rotten Oranges problem, we can use a Breadth-First Search (BFS) algorithm to simulate the
rotting process. We start by adding all rotten oranges to the queue. Then, we perform BFS to propagate the
rotting process to adjacent fresh oranges. We keep track of time (minutes) during the process and return the
time when all oranges are rotten.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for solving the Rotten Oranges problem:

Start
|
V
Initialize variables: grid (matrix), queue (for BFS), time (initialized to 0)
|
V
Enqueue all rotten oranges into the queue
|
V
While the queue is not empty
|----|
| V
| For each rotten orange in the queue
| |----|
| | V
| | Dequeue the rotten orange and mark adjacent fresh oranges as rotten
| | |
| | V
| | If any fresh oranges were marked as rotten
| | |----|
| | | V
| | | Enqueue them into the queue
| | | |
| | | V
| | | Increment time (minutes)
| | |----|
| | |
| |----|
|
|----|
|
V
If there are any fresh oranges left, return -1 (impossible to rot all)
|
V
Return the time (minutes) it took to rot all oranges
|
V
End
C++ Code (Solving the Rotten Oranges Problem):
Here's a C++ program to find the minimum time required to rot all oranges in a grid using BFS:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int rotOranges(vector<vector<int>>& grid) {


int rows = grid.size();
int cols = grid[0].size();
queue<pair<int, int>> rottenQueue;
int freshCount = 0;
int minutes = 0;

// Initialize the queue and count fresh oranges


for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == 2) {
rottenQueue.push({i, j});
} else if (grid[i][j] == 1) {
freshCount++;
}
}
}

// Define possible movement directions


vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

while (!rottenQueue.empty()) {
int queueSize = rottenQueue.size();

for (int i = 0; i < queueSize; i++) {


int row = rottenQueue.front().first;
int col = rottenQueue.front().second;
rottenQueue.pop();

for (auto& direction : directions) {


int newRow = row + direction.first;
int newCol = col + direction.second;

if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols &&
grid[newRow][newCol] == 1) {
grid[newRow][newCol] = 2;
rottenQueue.push({newRow, newCol});
freshCount--;
}
}
}

if (!rottenQueue.empty()) {
minutes++;
}
}
if (freshCount > 0) {
return -1; // Some oranges cannot be rotten
}

return minutes;
}

int main() {
vector<vector<int>> grid = {
{2, 1, 1},
{1, 1, 0},
{0, 1, 1}
};

int result = rotOranges(grid);

if (result == -1) {
cout << "It's impossible to rot all oranges." << endl;
} else {
cout << "The minimum time to rot all oranges is: " << result << " minutes." << endl;
}

return 0;
}
Output

Sample Output (Possible):

The minimum time to rot all oranges is: 4 minutes.

Conclusion:
The Rotten Oranges problem is a classic application of BFS in simulating a process. This algorithm
efficiently calculates the minimum time required to rot all oranges, considering the grid and the rotting
process. If it's impossible to rot all oranges, it returns -1. This problem is commonly used in programming
interviews and real-world scenarios involving time-dependent processes.
Aim:
The aim of this algorithm is to find the minimum number of swaps required to sort an unsorted array into
ascending order.

Theory:
To solve the "minimum swaps to sort" problem, we can use various sorting algorithms and count the swaps
made during the sorting process. The goal is to find the minimum number of swaps to achieve the sorted
order.

Simplified Flowchart:
Here's a simplified flowchart representing the algorithm for finding the minimum number of swaps to sort
an array:

Start
|
V
Initialize variables: array (unsorted), sortedArray (a copy of the sorted array), swaps (initialized to 0)
|
V
Sort the array using a sorting algorithm, counting the swaps
|
V
Compare the sorted array with the original array
|
V
If they are the same, return the number of swaps
|
V
If they are different, return "Not sortable" (impossible to sort with swaps)
|
V
End

C++ Code (Finding Minimum Swaps to Sort an Array):


Here's a C++ program to find the minimum number of swaps required to sort an array into ascending order:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int minSwapsToSort(vector<int>& arr) {


int n = arr.size();
vector<pair<int, int>> indexedArray(n);

for (int i = 0; i < n; i++) {


indexedArray[i] = {arr[i], i};
}

sort(indexedArray.begin(), indexedArray.end());

vector<bool> visited(n, false);


int swaps = 0;
for (int i = 0; i < n; i++) {
if (visited[i] || indexedArray[i].second == i) {
continue;
}

int cycleSize = 0;
int j = i;

while (!visited[j]) {
visited[j] = true;
j = indexedArray[j].second;
cycleSize++;
}

if (cycleSize > 0) {
swaps += cycleSize - 1;
}
}

return swaps;
}

int main() {
vector<int> unsortedArray = {4, 3, 2, 1};
int swaps = minSwapsToSort(unsortedArray);

if (swaps == -1) {
cout << "Not sortable with swaps." << endl;
} else {
cout << "Minimum swaps required to sort: " << swaps << endl;
}

return 0;
}

Output:

Sample Output (Possible):


Minimum swaps required to sort: 2

Conclusion:
The algorithm for finding the minimum swaps required to sort an array into ascending order is essential in
various applications. It efficiently counts the minimum number of swaps required to achieve a sorted order,
making it useful for optimizing sorting algorithms and identifying the difficulty of sorting a particular
sequence. If the output is "Not sortable with swaps," it means that the array cannot be sorted with swaps
alone.

You might also like