From 4f10cbb5f32f284a52b0656e636655e4cc24a4c3 Mon Sep 17 00:00:00 2001 From: Harshil Shah <143382356+HarshilShah1804@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:29:42 +0530 Subject: [PATCH 1/5] Modified docs,test of topological sort algorithm --- graph/topological_sort.cpp | 175 +++++++++++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 37 deletions(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 5de8ed69e7f..191dff5460c 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -1,50 +1,151 @@ -#include -#include -#include - -int number_of_vertices, - number_of_edges; // For number of Vertices (V) and number of edges (E) -std::vector> graph; -std::vector visited; -std::vector topological_order; - -void dfs(int v) { - visited[v] = true; - for (int u : graph[v]) { - if (!visited[u]) { - dfs(u); +/** + * @file + * @brief [Topological Sort + * Algorithm](https://en.wikipedia.org/wiki/Topological_sorting) + * @details + * Topological sorting of a directed graph is a linear ordering or its vertices + * such that for every directed edge (u,v) from vertex u to vertex v, u comes + * before v in the oredering. + * + * A topological sort is possible only in a directed acyclic graph (DAG). + * This file contains code of finding topological sort using Kahn's Algorithm + * which involves using Depth First Search technique + */ + +#include // For std::reverse +#include // For assert +#include // For IO operations +#include // For std::stack +#include // For std::invalid_argument +#include // For std::vector + +/** + * @namespace graph + * @brief Graph algorithms + */ +namespace graph { + +/** + * @brief Function that add edge between two nodes or vertices of graph + * @param u any node or vertex of graph + * @param v any node or vertex of graph + * @returns None + */ +void addEdge(std::vector>& adj, int u, int v) { + adj[u].push_back(v); +} + +/** + * @brief Function to perform Depth First Search on the graph + * @param v starting vertex for depth first search + * @param vistied array representing if a node is visited + * @param graph adjecancy list of the graph + * @param s stack containing the vertex of topological sort + * @returns None + */ +void dfs(int v, std::vector& visited, std::vector>& graph, + std::stack& s) { + visited[v] = 1; + for (int i = 0; i < graph[v].size(); i++) { + int neighbour = graph[v][i]; + if (!visited[neighbour]) { + dfs(neighbour, visited, graph, s); } } - topological_order.push_back(v); + s.push(v); } -void topological_sort() { - visited.assign(number_of_vertices, false); - topological_order.clear(); - for (int i = 0; i < number_of_vertices; ++i) { +/** + * @brief Function to get the topological sort of the graph + * @param graph adjecancy list of the graph + * @param n number of nodes in the graph + */ +std::vector topological_sort(std::vector>& graph, int n) { + std::vector visited(n, 0); + std::stack s; + for (int i = 0; i < n; i++) { if (!visited[i]) { - dfs(i); + dfs(i, visited, graph, s); } } - reverse(topological_order.begin(), topological_order.end()); + std::vector ans; + while (!s.empty()) { + int elem = s.top(); + s.pop(); + ans.push_back(elem); + } + if (ans.size() < n) { // Cycle detected + throw std::invalid_argument("cycle present in graph"); + } + return ans; } -int main() { - std::cout - << "Enter the number of vertices and the number of directed edges\n"; - std::cin >> number_of_vertices >> number_of_edges; - int x = 0, y = 0; - graph.resize(number_of_vertices, std::vector()); - for (int i = 0; i < number_of_edges; ++i) { - std::cin >> x >> y; - x--, y--; // to convert 1-indexed to 0-indexed - graph[x].push_back(y); +} // namespace graph + +/** + * @brief Self-test implementation + * @returns void + */ +static void test() { + // Test 1; + std::cout << "Testing for graph 1\n"; + int n_1 = 6; + std::vector> adj_1(n_1); + graph::addEdge(adj_1, 4, 0); + graph::addEdge(adj_1, 5, 0); + graph::addEdge(adj_1, 5, 2); + graph::addEdge(adj_1, 2, 3); + graph::addEdge(adj_1, 3, 1); + graph::addEdge(adj_1, 4, 1); + std::vector ans_1 = graph::topological_sort(adj_1, n_1); + std::vector expected_1 = {5, 4, 2, 3, 1, 0}; + std::cout << "Topological Sorting Order: "; + for (int i : ans_1) { + std::cout << i << " "; } - topological_sort(); - std::cout << "Topological Order : \n"; - for (int v : topological_order) { - std::cout << v + 1 - << ' '; // converting zero based indexing back to one based. + std::cout << '\n'; + assert(ans_1 == expected_1); + std::cout << "Test Passed\n\n"; + + // Test 2 + std::cout << "Testing for graph 2\n"; + int n_2 = 5; + std::vector> adj_2(n_2); + graph::addEdge(adj_2, 0, 1); + graph::addEdge(adj_2, 0, 2); + graph::addEdge(adj_2, 1, 2); + graph::addEdge(adj_2, 2, 3); + graph::addEdge(adj_2, 1, 3); + graph::addEdge(adj_2, 2, 4); + std::vector ans_2 = graph::topological_sort(adj_2, n_2); + std::vector expected_2 = {0, 1, 2, 4, 3}; + std::cout << "Topological Sorting Order: "; + for (int i : ans_2) { + std::cout << i << " "; } std::cout << '\n'; + assert(ans_2 == expected_2); + std::cout << "Test Passed\n\n"; + + // Test 3 - Graph with cycle + std::cout << "Testing for graph 3\n"; + int n_3 = 3; + std::vector> adj_3(n_3); + graph::addEdge(adj_3, 0, 1); + graph::addEdge(adj_3, 1, 2); + graph::addEdge(adj_3, 2, 0); + try { + graph::topological_sort(adj_3, n_3); + } catch (std::invalid_argument& err) { + assert(std::string(err.what()) == "cycle detected in graph"); + } + std::cout << "Test Passed \n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); return 0; } From d9fc6e197789d8d5b76ade9edc951a6403526b5b Mon Sep 17 00:00:00 2001 From: Harshil Shah <143382356+HarshilShah1804@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:32:17 +0530 Subject: [PATCH 2/5] Update topological_sort.cpp --- graph/topological_sort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 191dff5460c..626a65effa3 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -146,6 +146,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); + test(); // Self - test implementation return 0; } From 562548094c549ae17d2c15267bff5a74b26a400f Mon Sep 17 00:00:00 2001 From: Harshil Shah <143382356+HarshilShah1804@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:02:05 +0530 Subject: [PATCH 3/5] Add class "Graph" --- graph/topological_sort.cpp | 122 +++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 626a65effa3..8afc279090e 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -26,28 +26,55 @@ namespace graph { /** - * @brief Function that add edge between two nodes or vertices of graph - * @param u any node or vertex of graph - * @param v any node or vertex of graph - * @returns None + * @class Graph + * @brief Class that represents a directed graph and provides methods for + * manipulating the graph */ -void addEdge(std::vector>& adj, int u, int v) { - adj[u].push_back(v); -} +class Graph { + private: + int n; // Number of nodes + std::vector> adj; // Adjacency list representation + + public: + /** + * @brief Constructor for the Graph class + * @param nodes Number of nodes in the graph + */ + Graph(int nodes) : n(nodes), adj(nodes) {} + + /** + * @brief Function that adds an edge between two nodes or vertices of graph + * @param u Start node of the edge + * @param v End node of the edge + */ + void addEdge(int u, int v) { adj[u].push_back(v); } + + /** + * @brief Get the adjacency list of the graph + * @returns A reference to the adjacency list + */ + const std::vector>& getAdjacencyList() const { + return adj; + } + + /** + * @brief Get the number of nodes in the graph + * @returns The number of nodes + */ + int getNumNodes() const { return n; } +}; /** * @brief Function to perform Depth First Search on the graph - * @param v starting vertex for depth first search - * @param vistied array representing if a node is visited - * @param graph adjecancy list of the graph - * @param s stack containing the vertex of topological sort - * @returns None + * @param v Starting vertex for depth-first search + * @param visited Array representing whether each node has been visited + * @param graph Adjacency list of the graph + * @param s Stack containing the vertices for topological sorting */ -void dfs(int v, std::vector& visited, std::vector>& graph, - std::stack& s) { +void dfs(int v, std::vector& visited, + const std::vector>& graph, std::stack& s) { visited[v] = 1; - for (int i = 0; i < graph[v].size(); i++) { - int neighbour = graph[v][i]; + for (int neighbour : graph[v]) { if (!visited[neighbour]) { dfs(neighbour, visited, graph, s); } @@ -57,25 +84,30 @@ void dfs(int v, std::vector& visited, std::vector>& graph, /** * @brief Function to get the topological sort of the graph - * @param graph adjecancy list of the graph - * @param n number of nodes in the graph + * @param g Graph object + * @returns A vector containing the topological order of nodes */ -std::vector topological_sort(std::vector>& graph, int n) { +std::vector topologicalSort(const Graph& g) { + int n = g.getNumNodes(); + const auto& adj = g.getAdjacencyList(); std::vector visited(n, 0); std::stack s; + for (int i = 0; i < n; i++) { if (!visited[i]) { - dfs(i, visited, graph, s); + dfs(i, visited, adj, s); } } + std::vector ans; while (!s.empty()) { int elem = s.top(); s.pop(); ans.push_back(elem); } + if (ans.size() < n) { // Cycle detected - throw std::invalid_argument("cycle present in graph"); + throw std::invalid_argument("cycle detected in graph"); } return ans; } @@ -86,17 +118,17 @@ std::vector topological_sort(std::vector>& graph, int n) { * @returns void */ static void test() { - // Test 1; + // Test 1 std::cout << "Testing for graph 1\n"; int n_1 = 6; - std::vector> adj_1(n_1); - graph::addEdge(adj_1, 4, 0); - graph::addEdge(adj_1, 5, 0); - graph::addEdge(adj_1, 5, 2); - graph::addEdge(adj_1, 2, 3); - graph::addEdge(adj_1, 3, 1); - graph::addEdge(adj_1, 4, 1); - std::vector ans_1 = graph::topological_sort(adj_1, n_1); + graph::Graph graph1(n_1); + graph1.addEdge(4, 0); + graph1.addEdge(5, 0); + graph1.addEdge(5, 2); + graph1.addEdge(2, 3); + graph1.addEdge(3, 1); + graph1.addEdge(4, 1); + std::vector ans_1 = graph::topologicalSort(graph1); std::vector expected_1 = {5, 4, 2, 3, 1, 0}; std::cout << "Topological Sorting Order: "; for (int i : ans_1) { @@ -109,14 +141,14 @@ static void test() { // Test 2 std::cout << "Testing for graph 2\n"; int n_2 = 5; - std::vector> adj_2(n_2); - graph::addEdge(adj_2, 0, 1); - graph::addEdge(adj_2, 0, 2); - graph::addEdge(adj_2, 1, 2); - graph::addEdge(adj_2, 2, 3); - graph::addEdge(adj_2, 1, 3); - graph::addEdge(adj_2, 2, 4); - std::vector ans_2 = graph::topological_sort(adj_2, n_2); + graph::Graph graph2(n_2); + graph2.addEdge(0, 1); + graph2.addEdge(0, 2); + graph2.addEdge(1, 2); + graph2.addEdge(2, 3); + graph2.addEdge(1, 3); + graph2.addEdge(2, 4); + std::vector ans_2 = graph::topologicalSort(graph2); std::vector expected_2 = {0, 1, 2, 4, 3}; std::cout << "Topological Sorting Order: "; for (int i : ans_2) { @@ -129,16 +161,16 @@ static void test() { // Test 3 - Graph with cycle std::cout << "Testing for graph 3\n"; int n_3 = 3; - std::vector> adj_3(n_3); - graph::addEdge(adj_3, 0, 1); - graph::addEdge(adj_3, 1, 2); - graph::addEdge(adj_3, 2, 0); + graph::Graph graph3(n_3); + graph3.addEdge(0, 1); + graph3.addEdge(1, 2); + graph3.addEdge(2, 0); try { - graph::topological_sort(adj_3, n_3); + graph::topologicalSort(graph3); } catch (std::invalid_argument& err) { assert(std::string(err.what()) == "cycle detected in graph"); } - std::cout << "Test Passed \n"; + std::cout << "Test Passed\n"; } /** @@ -146,6 +178,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); // Self - test implementation + test(); return 0; } From 457c03caeae7d03c889971f3a8fa105313230da6 Mon Sep 17 00:00:00 2001 From: Harshil Shah <143382356+HarshilShah1804@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:53:38 +0530 Subject: [PATCH 4/5] Update graph/topological_sort.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- graph/topological_sort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 8afc279090e..74c4a919c55 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -178,6 +178,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); + test(); // run self test implementations return 0; } From 75bd9a3fdb6a696e2cca3a1f5b8663747566c495 Mon Sep 17 00:00:00 2001 From: Harshil Shah <143382356+HarshilShah1804@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:59:53 +0530 Subject: [PATCH 5/5] Add namespace topological_sort --- graph/topological_sort.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 74c4a919c55..6ff81b473f1 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -25,6 +25,11 @@ */ namespace graph { +/** + * @namespace topological_sort + * @brief Topological Sort Algorithm + */ +namespace topological_sort { /** * @class Graph * @brief Class that represents a directed graph and provides methods for @@ -111,6 +116,7 @@ std::vector topologicalSort(const Graph& g) { } return ans; } +} // namespace topological_sort } // namespace graph /** @@ -121,14 +127,14 @@ static void test() { // Test 1 std::cout << "Testing for graph 1\n"; int n_1 = 6; - graph::Graph graph1(n_1); + graph::topological_sort::Graph graph1(n_1); graph1.addEdge(4, 0); graph1.addEdge(5, 0); graph1.addEdge(5, 2); graph1.addEdge(2, 3); graph1.addEdge(3, 1); graph1.addEdge(4, 1); - std::vector ans_1 = graph::topologicalSort(graph1); + std::vector ans_1 = graph::topological_sort::topologicalSort(graph1); std::vector expected_1 = {5, 4, 2, 3, 1, 0}; std::cout << "Topological Sorting Order: "; for (int i : ans_1) { @@ -141,14 +147,14 @@ static void test() { // Test 2 std::cout << "Testing for graph 2\n"; int n_2 = 5; - graph::Graph graph2(n_2); + graph::topological_sort::Graph graph2(n_2); graph2.addEdge(0, 1); graph2.addEdge(0, 2); graph2.addEdge(1, 2); graph2.addEdge(2, 3); graph2.addEdge(1, 3); graph2.addEdge(2, 4); - std::vector ans_2 = graph::topologicalSort(graph2); + std::vector ans_2 = graph::topological_sort::topologicalSort(graph2); std::vector expected_2 = {0, 1, 2, 4, 3}; std::cout << "Topological Sorting Order: "; for (int i : ans_2) { @@ -161,12 +167,12 @@ static void test() { // Test 3 - Graph with cycle std::cout << "Testing for graph 3\n"; int n_3 = 3; - graph::Graph graph3(n_3); + graph::topological_sort::Graph graph3(n_3); graph3.addEdge(0, 1); graph3.addEdge(1, 2); graph3.addEdge(2, 0); try { - graph::topologicalSort(graph3); + graph::topological_sort::topologicalSort(graph3); } catch (std::invalid_argument& err) { assert(std::string(err.what()) == "cycle detected in graph"); } @@ -178,6 +184,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); // run self test implementations + test(); // run self test implementations return 0; }