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 10 commits into
base: master
Choose a base branch
from
131 changes: 131 additions & 0 deletions graph/number_of_paths.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
* @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
#include <cstdint> /// for fixed-size integer types (e.g., std::uint32_t)

/**
* @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`
*/
std::uint32_t count_paths_dfs(const std::vector<std::vector<std::uint32_t>>& A,
std::uint32_t u,
std::uint32_t v,
std::uint32_t n,
std::vector<bool>& visited) {
if (u == v) {
return 1; // Base case: Reached the destination node
}

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

for (std::uint32_t 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`
*/
std::uint32_t count_paths(const std::vector<std::vector<std::uint32_t>>& A,
std::uint32_t u,
std::uint32_t v,
std::uint32_t n) {
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<std::uint32_t>> 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
};
std::uint32_t 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<std::uint32_t>> 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}
};
std::uint32_t 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<std::uint32_t>> 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}
};
std::uint32_t n3 = 5, u3 = 0, v3 = 4;
assert(graph::count_paths(graph3, u3, v3, n3) == 3); // There are 3 paths from node 0 to 4

realstealthninja marked this conversation as resolved.
Show resolved Hide resolved
// Test case 4: Single node graph (self-loop)
std::vector<std::vector<std::uint32_t>> graph4 = {
{0}
};
std::uint32_t 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;
}