From 91526d3b02faded949bc79f2148c37a7dd80bbd0 Mon Sep 17 00:00:00 2001 From: Aditya Borate <23110065@iitgn.ac.in> Date: Fri, 11 Oct 2024 19:28:31 +0530 Subject: [PATCH 1/5] feat: add count_paths algorithm with a test case --- graph/number_of_paths.cpp | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 graph/number_of_paths.cpp diff --git a/graph/number_of_paths.cpp b/graph/number_of_paths.cpp new file mode 100644 index 00000000000..a09add06028 --- /dev/null +++ b/graph/number_of_paths.cpp @@ -0,0 +1,78 @@ +/** + * @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 /// for std::vector +#include /// for IO operations +#include /// for assert + +/** + * @namespace graph + * @brief Graph algorithms + */ +namespace graph { + + /** + * @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> &A, int u, int v, int n) { + if (u == v) { + return 1; // Base case: Reached the destination node + } + + int path_count = 0; // Count of all paths from `u` to `v` + + for (int i = 0; i < n; i++) { + if (A[u][i] == 1) { // Check if there is an edge from `u` to `i` + path_count += count_paths(A, i, v, n); // Recursively explore paths from `i` to `v` + } + } + + return path_count; + } + +} // namespace graph + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + // Test case 1: Simple directed graph with multiple paths + std::vector> 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} + }; + 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 + std::cout << "All tests have successfully passed!\n"; +} + + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // Run self-test implementations + return 0; +} From 13fff24f42f9ce58119a9698a3b5364e2b095e2a Mon Sep 17 00:00:00 2001 From: Aditya Borate <23110065@iitgn.ac.in> Date: Tue, 15 Oct 2024 20:30:54 +0530 Subject: [PATCH 2/5] fix: updated number_of_paths algorithm, added more test cases, and set unsigned int as parameter --- graph/number_of_paths.cpp | 61 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/graph/number_of_paths.cpp b/graph/number_of_paths.cpp index a09add06028..c9b17f116ce 100644 --- a/graph/number_of_paths.cpp +++ b/graph/number_of_paths.cpp @@ -22,31 +22,47 @@ namespace graph { /** - * @brief Counts the number of paths from node `u` to node `v` in a directed graph - * using Depth First Search (DFS) - * + * @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(const std::vector> &A, int u, int v, int n) { + int count_paths_dfs(const std::vector>& A, int u, int v, int n, std::vector& visited) { 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) { // Check if there is an edge from `u` to `i` - path_count += count_paths(A, i, v, n); // Recursively explore paths from `i` to `v` + 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>& A, int u, int v, int n) { + std::vector visited(n, false); // Initialize a visited vector for tracking nodes + return count_paths_dfs(A, u, v, n, visited); + } + } // namespace graph /** @@ -55,7 +71,7 @@ namespace graph { */ static void test() { // Test case 1: Simple directed graph with multiple paths - std::vector> graph1 = { + std::vector> graph1 = { {0, 1, 0, 1, 0}, {0, 0, 1, 0, 1}, {0, 0, 0, 0, 1}, @@ -64,10 +80,39 @@ static void test() { }; 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> 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> 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 + + // Test case 4: Single node graph (self-loop) + std::vector> 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 From 4f981e8c36851b3fa4bb592651f6e030490131c9 Mon Sep 17 00:00:00 2001 From: Aditya Borate <23110065@iitgn.ac.in> Date: Sat, 19 Oct 2024 17:41:02 +0530 Subject: [PATCH 3/5] fix: replaced unsigned int with std::uint32_t for fixed size --- graph/number_of_paths.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/graph/number_of_paths.cpp b/graph/number_of_paths.cpp index c9b17f116ce..c5a3f55689c 100644 --- a/graph/number_of_paths.cpp +++ b/graph/number_of_paths.cpp @@ -14,6 +14,7 @@ #include /// for std::vector #include /// for IO operations #include /// for assert +#include /// for fixed-size integer types (e.g., std::uint32_t) /** * @namespace graph @@ -30,15 +31,19 @@ namespace 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>& A, int u, int v, int n, std::vector& visited) { + std::uint32_t count_paths_dfs(const std::vector>& A, + std::uint32_t u, + std::uint32_t v, + std::uint32_t n, + std::vector& visited) { 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` + std::uint32_t path_count = 0; // Count of all paths from `u` to `v` - for (int i = 0; i < n; i++) { + 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` } @@ -58,7 +63,10 @@ namespace graph { * @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>& A, int u, int v, int n) { + std::uint32_t count_paths(const std::vector>& A, + std::uint32_t u, + std::uint32_t v, + std::uint32_t n) { std::vector visited(n, false); // Initialize a visited vector for tracking nodes return count_paths_dfs(A, u, v, n, visited); } @@ -71,43 +79,43 @@ namespace graph { */ static void test() { // Test case 1: Simple directed graph with multiple paths - std::vector> graph1 = { + std::vector> 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} }; - int n1 = 5, u1 = 0, v1 = 4; + 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> graph2 = { + std::vector> 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; + 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> graph3 = { + std::vector> 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; + 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 // Test case 4: Single node graph (self-loop) - std::vector> graph4 = { + std::vector> graph4 = { {0} }; - int n4 = 1, u4 = 0, v4 = 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"; From 12c94c126f6fd10f0383b7471814265ecf314f7f Mon Sep 17 00:00:00 2001 From: Aditya Borate <23110065@iitgn.ac.in> Date: Mon, 21 Oct 2024 22:28:24 +0530 Subject: [PATCH 4/5] fix: Handled empty graph, invalid input and added test cases for the same --- graph/number_of_paths.cpp | 50 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/graph/number_of_paths.cpp b/graph/number_of_paths.cpp index c5a3f55689c..45774021404 100644 --- a/graph/number_of_paths.cpp +++ b/graph/number_of_paths.cpp @@ -32,27 +32,28 @@ namespace graph { * @returns the number of paths from node `u` to node `v` */ std::uint32_t count_paths_dfs(const std::vector>& A, - std::uint32_t u, - std::uint32_t v, - std::uint32_t n, - std::vector& visited) { + std::uint32_t u, + std::uint32_t v, + std::uint32_t n, + std::vector& visited) { if (u == v) { - return 1; // Base case: Reached the destination node + 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` + 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` + 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) + 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) @@ -64,11 +65,16 @@ namespace graph { * @returns the number of paths from node `u` to node `v` */ std::uint32_t count_paths(const std::vector>& A, - std::uint32_t u, - std::uint32_t v, - std::uint32_t n) { - std::vector visited(n, false); // Initialize a visited vector for tracking nodes - return count_paths_dfs(A, u, v, n, visited); + std::uint32_t u, + std::uint32_t v, + std::uint32_t n) { + // Check for invalid nodes or empty graph + if (u >= n || v >= n || A.empty() || A[0].empty()) { + return 0; // No valid paths if graph is empty or nodes are out of bounds + } + + std::vector visited(n, false); // Initialize a visited vector for tracking nodes + return count_paths_dfs(A, u, v, n, visited); // Start DFS } } // namespace graph @@ -118,6 +124,20 @@ static void test() { 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 + // Test case 5: Empty graph (no nodes, no paths) + std::vector> graph5 = {{}}; + int n5 = 0, u5 = 0, v5 = 0; + assert(graph::count_paths(graph5, u5, v5, n5) == 0); // There are no paths in an empty graph + + // Test case 6: Invalid nodes (out of bounds) + std::vector> graph6 = { + {0, 1, 0}, + {0, 0, 1}, + {0, 0, 0} + }; + int n6 = 3, u6 = 0, v6 = 5; // Node `v` is out of bounds (n = 3, so valid indices are 0, 1, 2) + assert(graph::count_paths(graph6, u6, v6, n6) == 0); // Should return 0 because `v = 5` is invalid + std::cout << "All tests have successfully passed!\n"; } From 65e03d63a7b071dd9e2469da83a99663c79d781c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 31 Oct 2024 05:17:41 +0000 Subject: [PATCH 5/5] clang-format and clang-tidy fixes for 80e27baa --- .../{Unbounded_0_1_Knapsack.cpp => unbounded_0_1_knapsack.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dynamic_programming/{Unbounded_0_1_Knapsack.cpp => unbounded_0_1_knapsack.cpp} (100%) diff --git a/dynamic_programming/Unbounded_0_1_Knapsack.cpp b/dynamic_programming/unbounded_0_1_knapsack.cpp similarity index 100% rename from dynamic_programming/Unbounded_0_1_Knapsack.cpp rename to dynamic_programming/unbounded_0_1_knapsack.cpp