Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Depth first search #2815

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
123 changes: 123 additions & 0 deletions graph/number_of_paths.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* @file
* @brief Algorithm to count paths between two nodes in a directed graph using DFS
* @details
* This algorithm implements Depth First Search (DFS) to count the number of
* possible paths between two nodes in a directed graph. It is represented using
* an adjacency matrix. The algorithm recursively traverses the graph to find
* all paths from the source node `u` to the destination node `v`.
*
* @author [Aditya Borate](https://github.com/adi776borate)
* @see https://en.wikipedia.org/wiki/Path_(graph_theory)
*/

#include <vector> /// for std::vector
#include <iostream> /// for IO operations
#include <cassert> /// for assert

/**
* @namespace graph
* @brief Graph algorithms
*/
namespace graph {

/**
* @brief Helper function to perform DFS and count the number of paths from node `u` to node `v`
* @param A adjacency matrix representing the graph (1: edge exists, 0: no edge)
* @param u the starting node
* @param v the destination node
* @param n the number of nodes in the graph
* @param visited a vector to keep track of visited nodes in the current DFS path
* @returns the number of paths from node `u` to node `v`
*/
int count_paths_dfs(const std::vector<std::vector<unsigned int>>& A, int u, int v, int n, std::vector<bool>& visited) {
realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
if (u == v) {
return 1; // Base case: Reached the destination node
}

visited[u] = true; // Mark the current node as visited
int path_count = 0; // Count of all paths from `u` to `v`

for (int i = 0; i < n; i++) {
if (A[u][i] == 1 && !visited[i]) { // Check if there is an edge and the node is not visited
path_count += count_paths_dfs(A, i, v, n, visited); // Recursively explore paths from `i` to `v`
}
}

visited[u] = false; // Unmark the current node as visited (backtracking)
return path_count;
}

/**
* @brief Counts the number of paths from node `u` to node `v` in a directed graph
* using Depth First Search (DFS)
*
* @param A adjacency matrix representing the graph (1: edge exists, 0: no edge)
* @param u the starting node
* @param v the destination node
* @param n the number of nodes in the graph
* @returns the number of paths from node `u` to node `v`
*/
int count_paths(const std::vector<std::vector<unsigned int>>& A, int u, int v, int n) {
realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
std::vector<bool> visited(n, false); // Initialize a visited vector for tracking nodes
return count_paths_dfs(A, u, v, n, visited);
}

} // namespace graph

/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// Test case 1: Simple directed graph with multiple paths
std::vector<std::vector<unsigned int>> graph1 = {
{0, 1, 0, 1, 0},
{0, 0, 1, 0, 1},
{0, 0, 0, 0, 1},
{0, 0, 1, 0, 0},
{0, 0, 0, 0, 0}
realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
};
int n1 = 5, u1 = 0, v1 = 4;
assert(graph::count_paths(graph1, u1, v1, n1) == 3); // There are 3 paths from node 0 to 4

// Test case 2: No possible path (disconnected graph)
std::vector<std::vector<unsigned int>> graph2 = {
{0, 1, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 1},
{0, 0, 1, 0, 0},
{0, 0, 0, 0, 0}
};
int n2 = 5, u2 = 0, v2 = 4;
assert(graph::count_paths(graph2, u2, v2, n2) == 0); // No path from node 0 to 4

// Test case 3: Cyclic graph with multiple paths
std::vector<std::vector<unsigned int>> graph3 = {
{0, 1, 0, 0, 0},
{0, 0, 1, 1, 0},
{1, 0, 0, 0, 1},
{0, 0, 1, 0, 1},
{0, 0, 0, 0, 0}
};
int n3 = 5, u3 = 0, v3 = 4;
assert(graph::count_paths(graph3, u3, v3, n3) == 3); // There are 3 paths from node 0 to 4

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a test case for empty array
std::vector<std::vectorstd::uint32_t> graph5 = {{}};

this should return 0 as there is no path

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the latest changes as per your feedback. I hope this meets the requirements now. Looking forward to your review.

// Test case 4: Single node graph (self-loop)
std::vector<std::vector<unsigned int>> graph4 = {
{0}
};
int n4 = 1, u4 = 0, v4 = 0;
assert(graph::count_paths(graph4, u4, v4, n4) == 1); // There is self-loop, so 1 path from node 0 to 0

std::cout << "All tests have successfully passed!\n";
}

/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // Run self-test implementations
return 0;
}